How do I use AJAX for pagination in Django-Tables2? - django

I am using django-tables2 and I am trying to display the data of the next page without loading another page. I think AJAX could be used.
From what I have found, it seems that it might not be possible.
There has been some discussion about this Support AJAX sorting/pagination
Is there something that I should look at to figure it out ?

It is possible however it's not so easy since django-tables2 (and django in general) are geared more to the Server Side Rendered world. I will sketch a solution here and consider that interesting topic for my blog (https://spapas.github.io):
You'll need to override the django-tables2 template that'll be used. You can't use the default one since the pagination to it is done by normal links. You should override it to use Ajax calls - take a look at this question for more Is it possible to custom django-tables2 template for a specific page in this case?. What you have to do is disable the default link functionality of the page links and call them through ajax. Depending on how you're going to do it this may be possible to be done through a script in your view's template without the need to override the table template at all.
You'll need to modify your view to detect if it's called by ajax (i.e through these pagination buttons you've defined above) or not and return a different template each time. If it's called normally then you'll just render your classic template. If it is called through ajax then it will return only the portion of the html that only contains the table.
Now in your normal template you'll put the table inside a div named (f.e) #the_table - when the user clicks on a pagination link you'll do the ajax call and the response will contain only the table - you'll then replace the contents of the #the_table div with what you just received.
So you should have two templates for your view, your view.html which will be something like:
{% extends base.html %}
{% block content %}
blah blah
<div id='the_table'>
{% include 'partial_table.html' %}
</div>
{% endblock %}
{% block extra_script %}
<script>
// You can do something like (NOT TESTED):
// I don't remember if the pagination links belong to a class
// if not you may want to add that class yourself
$('pagination-link').click(function(e) {
// Don't follow the link
e.preventDefault();
// Do the ajax request
$.get($(this).attr("href"), function(data) {
// Replace the table with what you received
$('#the_table').html(data)
});
});
</script>
{% endblock %}
And your partial_table.html:
{% load django_tables2 %}
{% render_table table %}
Now in your view if for example you are using CBVs then you'll have to use the template_name = view.html as defined above and override get_template_names like this:
def get_template_names(self):
# Sometimes the is_ajax() is not working properly so if it doesn't
# just pass the ajax_partial query parameter to your ajax request
if self.request.is_ajax() or self.request.GET.get('ajax_partial'):
return 'partial_table.html'
return super().get_template_names()
More info can be found in this recipe at my Django CBV guide: https://spapas.github.io/2018/03/19/comprehensive-django-cbv-guide/#implement-a-partial-ajax-view

Related

How can I prevent duplicate db queries when using Django's inclusion tag?

So imagine if I have widget that I registered as an inclusion tag like this:
#register.inclusion_tag('tags/widget.html', takes_context=True)
def widget(context):
return {'qs': Foo.objects.all(),
'request': context['request'],}
In the widget template I loop over some entries in the Foo object. I include the widget in my Mega Menu and my Footer. These templates are obviously split up. And included in base.html
However right now when I load the main page I hit the database twice for the same widget (Menu and Footer) so I get a duplicate query. How can I prevent something like this?
I like the idea of the inclusion tag but I don't want duplicate queries.
You can call your tag in base.html and save the data that it returns and then pass it to other sub templates that might need that data.
But i don't think you can use inclusion_tag anymore. You probably need to use something like simple_tag. Here is an example:
simple_tag:
#register.simple_tag
def widget(some_data):
return Foo.objects.all()
base template:
# You can access the result as foo_objs
{% widget some_data as foo_objs %}
# pass the data to other templates:
{% include "menu.html" with foo_objs=foo_objs %}
{% include "footer.html" with foo_objs=foo_objs %}
Now the widget function called once and data can be passed around.
Django docs on include

Check and clear filters with django-filter

I am using django-filter to filter a ListView and would like to display a "Clear all filters" link if any filters are applied.
Due to the generic nature of the filtering system I haven't yet found a straightforward way to achieve this.
The only thing I came up with so far is to return the regular queryset in the get_queryset method of the view if a "clear" flag is present in the request, however this doesn't actually clear the filters - it just returns all the data.
Does anyone have a solution/idea for this?
Update: Solution
After Jerin's comment I decided to solve this problem in 2 separate parts:
has filter:
I check if any of the fields I defined in my filter class are in the request. My solution looks a bit different as I'm using class based views so I abstracted it away in a mixin but if you're using simple views like here, you could just do:
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in
set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
clear all filters:
A simple redirect to your list view:
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Here is the mixup version of the answer (combination of mine and Chris)
You could place a Clear all filters button and that will redirect to your default ListView (/host/end/point/).
But some non-filter parameters (such as pagination or something else) may occur in URL. So the better option is, check for any filter fields in URL and if so, display the filter clearing link
The opted solution is,
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
and in template,
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Just make a button and point to the base search field.
<a class="btn btn-warning" href="{% url 'App:FilterView' %}">Reset</a>
If your FilterSet instance is available on the template you can check for filter.is_bound like this:
{% if filter.is_bound %}
Clear filters
{% endif %}
If you are using the FilterMixin or the FilterView, your FilterSet instance will be available as filter to the template as above.
I like this simple solution, however when I attempt to use it the current filter parameters are some how getting appended to the url even though it's the base url in the anchor.
So hovering over the button my link (determined using {% url 'app:view' %} shows
localhost/app/view correctly
However when clicking the button the url in browser has the parameters appended
localhost/app/view/?filter1=val1&filter2=val2 etc.
Is django caching something? Is browser (Chrome) caching? Can I force something in the anchor to not use them?
Answered my own, but for anyone else passing by:
I had the anchor on a button within a the filter form, although it was not a submit button moving it outside the form gave the desired result.

Include template displaying a form

What I want to do is include a form from a separate template at the bottom of a given page, lets say; "example.com/listdataandform/".
The form-template "form.html" displays the form as it should when the view is included in the URLConf. So I can view with "example.com/form/"
What I have so far goes something like this:
{% extends "base/base.html" %}
{% block title %} page title {% endblock %}
{% block content %}
<h2>some "scene" data</h2>
<ul>
{% for scene in scenes %}
<li>{{ scene.scene }} - {{ scene.date }}</li>
{% endfor %}
</ul>
{% include "tasks/form.html"%}
{% endblock %}
The code inside "block content" works as it should, since it is defined with it's corresponding view for the url "example.com/listdataandform/".
{% include "tasks/form.html"%}: This only displays the submit button from form.html, as expected. I realize by only doing this: {% include "tasks/form.html"%}, the corresponding view method is never executed to provide the "form"-template with data.
Is there any way to this without having to define the view to a specific pattern in urls.py, so that the form can be used without going to the that specified URL..?
So I guess the more general question is; how to include templates and provide them with data generated from a view?
Thanks.
For occasions like this, where I have something that needs to be included on every (or almost every) page, I use a custom context processor, which I then add to the TEMPLATE_CONTEXT_PROCESSORS in settings.py. You can add your form to the context by using this method.
Example:
common.py (this goes in the same folder as settings.py)
from myapp.forms import MyForm
def context(request):
c = {}
c['myform'] = MyForm()
return c
You can also do any processing required for the form here.
Then add it in your settings.py file:
settings.py
.
.
TEMPLATE_CONTEXT_PROCESSORS = (
'''
All the processors that are already there
'''
"myproject.common.context",
)
.
.
I realize by only doing this: {% include "tasks/form.html"%}, the corresponding view method is never executed to provide the "form"-template with data.
Indeed. You included a template, and it really means "included" - ie: "execute in the current context". The template knows nothing about your views, not even what a "view" is.
How does this help me executing the view for the included template to provide it with form data?
It doesn't. A Django "view" is not "a fraction of a template", it's really a request handler, iow a piece of code that takes an HTTP request and returns an HTTP response.
Your have to provide the form to the context one way or another. The possible places are:
in the view
in a context processor (if using a RequestContext)
in a middleware if using a TemplateResponse AND the TemplateResponse has not been rendered yet
in a custom template tag
In all cases this will just insert the form in your template's context - you'll still have to take care of the form processing when it's posted. There are different ways to address this problem but from what I guess of your use case (adding the same form and processing to a couple differents views of your own app), using a custom TemplateResponse subclass taking care of the form's initialisation and processing might just be the ticket.

Loading views into template without Ajax on Django

Good evening people. I have this Django question.. I have a view that renders a form with a header information from a master-detail like models. Below this form, I want to load another view that renders a form that fills the detail table and next to it, the list with the detail. I want to load inside the template of the header view, the detail's view but I want to do it without ajax. I was reading the {% include %} tag but, from what I understand, it loads a template, not a view. I might be wrong, is there a way to load a view inside the master's template?
Thanks in advance
You couldn't render a view like that without ajax. {% load %} is used for loading modules, not templates or views, like:
{% load humanize %}
However, you could use an {% include %} tag, or a custom template tag that does whatever processing you need and returns its template where you include it.

Django - two views, one page

Say we have a Django page that shows a list of items and allows the user to fill in a form to add to the items (let's call the items posts).
What I want:
The URL for this page refers to a view. The view calls two other views (called "sub-view" hereon), then each sub-view renders its section and returns the results. The main view then joins the results of the sub-views and returns that.
Ideally, I would have a quick javascript check on the page - if javascript is enabled, the submit button for the form will be "Ajax'd" to the sub-view that deals with form adding, and the page will be updated that way. I suppose I could trigger a request to refresh the list of posts afterwards too or something.
So how do I concatenate the two sub-views in the main view? Is this possible?
UPDATE: "sub-view" is a term I made up. What I want is a view that can be called either by Ajax directly to return something meaningful, or from another view (which I'll call the "main view"). If called by this "main view", how does the main view handle returning the data from multiple "sub-views"?
Is there a simple way to do this? Is this an appropriate way to think about multiple views in a page? Should I care about separation of responsibilities?
A view in django is just any callable that ultimately returns a Response object. Within that view, you could split the work up into whatever organization suits you. Maybe your view 100% delegates out to other methods.
In your case, your main view would call 2 other functions for data. These could also be views if they also accept a Request object as well and make use of it. They also would need to return Response objects to be considered django views since that is how you would point URLs at them. But it doesn't really do you any good to have two other views returning you Response objects. What you probably want is just other methods that do specific tasks and return some data structure, or maybe even a rendered snippet of a template. You would then use these data, or merge the template strings together and return that in your main Response.
If you are really set on making use of other views that return Response objects, then you can do something like grabbing the body out of them, and merging them into your own response:
https://docs.djangoproject.com/en/1.4/ref/request-response/
Really, nothing is much different from the tutorials. You are just calling other methods for data. If you want to make it organized you should separate the data processing logic from the view function. Your main view would call these data processing functions for values. And your "sub views" would just be simple views that also call these individual data functions and wrap them into a Response.
Pseudo:
def mainView(request):
val = data1()
val2 = data2()
response = # val + va2 + other stuff
return response
def subView1(request):
val = data1()
response = # val + stuff
return response
def subView2(request):
val2 = data2()
response = # val2 + stuff
return response
def data1():
val = # get data
return val
def data2():
val2 = # get data
return val2
The views should contain only view-related logic:
Views are for processing requests and serving the requested data
That includes checking for user authorization/permission and dealing with given parameters
If the requested data is not trivial to fetch, outsource the code to a more appropriate location (your model or form definitions or another custom place)
Outsource calculations to make them reusable and call these methods from your views to keep them small.
Nevertheless, maybe you want something else, namely templates with extends and include.
With extends you are able to create a base layout for your HTML code and define specific blocks which can be rendered elsewhere. Example? Ok.
base.html:
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<div id="header">
<h1>My Site</h1>
</div>
{% block content %}{% endblock %}
</body>
</html>
Then, in any other template, you can overwrite the blocks title and content which we defined in the base template:
{% extends "base.html" %}
{% block title %}My Page{% endblock %}
{% block content %}
<h2>My Page</h2>
<div>lorem ipsum</div>
{% endblock %}
Also, you can create sub-templates like the following one, let's name it _item.html:
<li class="item">
<span>{{ something.foo }}</span>
<strong>{{ something.bar }}</span>
</li>
You can include that snippet in any other template and pass an arbitrary number of parameters:
{% for something in mymodel.mym2mrelation.all %}
{% include "_item.html" with something=something only %}
{% endfor %}
Naturally, you can combine both concepts. Like so:
{% extends "base.html" %}
{% block title %}My Page{% endblock %}
{% block content %}
<h2>My Page</h2>
<div>lorem ipsum</div>
<ul>
{% for something in mymodel.mym2mrelation.all %}
{% include "_item.html" with something=something only %}
{% endfor %}
</ul>
{% endblock %}
I hope that helps.
This is the perfect time to introduce class based views.
class methods are essentially "sub views" that split logic into reusable snippets.
If you want the readability of split functions - it only gets better by using django's class based views (via all of the functionality provided by default and access to request, kwargs, args, etc., via the class instance).
The docs even contain a good example for returning a JSON response or HTML response based on request parameters (this exact situation).
The best part? You can reuse your class based views as mixins in future views. Look at the docs example to see how to convert any class based view to handle a JSON response of the template context via a simple subclass.