Does TemplateView supports pagination? - javascript

How can I implement pagination with Templateview? The below view is class-based view where I am using Templateview for listing out networks ( which I am calling from api from another application). Here I am not using models so I cant use Listview because I don't have queryset.
class classView(TemplateView):
template_name = 'view_network.html'
# paginate_by = 5
context_object_name = 'networks'
paginator = Paginator('networks', 5)
def get_context_data(self, **kwargs):
...
...
return context
in html page i have added this:
<span class="step-links" style="margin-left: 30%;
margin-bottom: 0px;
margin-top: 0px;
width: 100%;">
<div style="text-align: justify"></div>
<table style="width: 50%">
<tr>
{% if page_obj.has_previous %}
<td>« First</td>
<td> Previous</td>
{% endif %}
<td> Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</td>
{% if page_obj.has_next %}
<td>Next</td>
<td>Last »</td>
{% endif %}
</tr>
</table>
</div>
</span>
</div>

You can implement the pagination within the get_context_data() function. Assuming networks is a list of objects.
def get_context_data(self, **kwargs):
page_size = 5
paginator = Paginator(networks, page_size)
page = request.GET.get('page', 1)
page_obj = paginator.page(page)
context = {
"page_obj": page_obj
}

TemplateView does not support pagination, you have to use a ListView to use "paginate_by = 5" Here is a helpfull link to check all attributes/methods for each class based generic view.

Related

Can I Reorder a LIst Based On User Provided Values Using Only HTMX?

I have HTMX working. The code below is fully functional. The one piece I'd like to incorporate, I can't figure out how to do it. The user is able to provide a number rank...but when they click save, the view returns with the item at the top. Only after they click reload does the list sort itself based on how I defined the attributes with the model.
Here's my HTML...
<h1 class="title62">Tasks</h1>
<button class="button36" hx-get="{% url 'MyTasks:create_task_form' %}" hx-target="#taskforms">Add Task</button>
<div id="taskforms"></div>
<div id="tblData">
{% if tasks %}
{% for task in tasks %}
{% include "partials/task_detail.html" %}
{% endfor %}
</div>
{% endif %}
<div hx-target="this" hx-swap="outerHTML" hx-headers='{"X-CSRFToken":"{{ csrf_token }}"}' class="" >
<form method="POST">
{% csrf_token %}
<div class="table22">
<table class="table23">
<thead>
<tr>
<th class="title67">Number</th>
<th class="title67">Task</th>
</tr>
</thead>
<tbody>
<button class="button35" hx-post=".">
Save
</button>
<button type="button" class="button33">
Delete
</button>
<tr>
<td class="title73">{{ form.number }}</td>
<td class="title73">{{ form.task }}</td>
</tr>
</tbody>
</table>
</div>
</form>
</div>
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
<div hx-target="this" class="">
<div class="table22">
<table class="table23">
<thead>
<tr>
<th class="title67">Number</th>
<th class="title67">Task</th>
</tr>
</thead>
<tbody>
<button class="button35" hx-get="{% url 'MyTasks:update_task' task.id %}" hx-swap="outerHTML">
Update
</button>
<button class="button34" hx-confirm="Are you sure you want to delete?" hx-post="{% url 'MyTasks:delete_task' task.id %}" hx-swap="outerHTML">
Delete
</button>
<tr>
<td class="title70">{{ task.number }}</td>
<td class="title70">{{ task.task }}</td>
</tr>
</tbody>
</table>
</div>
</div>
My Model...
class Task(models.Model):
task = models.TextField(max_length=264,blank=True,null=True,unique=False)
number = models.PositiveIntegerField(default=1)
class Meta:
ordering = ["number"]
def __str__(self):
return self.task
I have begun to explore using Javascript to do HTML sorting to approach my issue that way instead. It just seems to me as capable as HTMX is there should be a way for me to do it leveraging HTMX. Thanks in advance for any thoughts.
I think the easiest way would be just to swap in the entire list on save instead of just the new item - that way you can take advantage of the ordering in the model.
EDIT:
In order to swap a list of the task in instead of the saved task, you will need to modify your view to return a list of the tasks instead of just the task being saved - like this for a CreateView:
class TaskCreateView(CreateView):
model = Task
form_class = TaskForm
template_name = 'task/_create_form.html'
def form_valid(self, form):
self.object = form.save()
tasks = Task.objects.all()
return render(self.request, 'task/_task_list.html', {'tasks': tasks})
You would also need to make partial template that would render all the tasks - something like this:
<div id="tblData">
{% if tasks %}
<ul>
{% for task in tasks %}
<li>{{ task.order }} - {{ task.task }}</li>
{% endfor %}
</ul>
{% endif %}
The HTMX technique you are looking for is the Out of Band Swapping which is basically 1 request with multiple targets. The targets can be anywhere on the page. In your case: when the user creates, changes or deletes a task, the response should also contain the updated list of tasks. It requires only a few modifications. For example a task updating view:
def update_view(request):
tasks_updated = False
tasks = None
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
# Update the task in the database
...
task.save()
tasks_updated = True
else:
# Return the form with errors
return render(request, 'task_form.html', {'form': form})
if tasks_updated:
# Fetch new list of tasks
tasks = Task.objects.order_by('number')
return render(request, 'task.html', {'tasks': tasks, 'task': task})
We have the tasks_updated tracking variable that we switch to True if we updated a task in the database, so the task list needs an update on the frontend. Just before we render the template, we check the value of this variable and fetch the tasks if needed.
And the partial template:
<div>
<!-- Render the task's table as usual. -->
</div>
{% if tasks %}
<div id="tblData" hx-swap-oob="true">
{% for task in tasks %}
{% include "partials/task_detail.html" %}
{% endfor %}
</div>
{% endif %}
Here we render the table only we have the tasks variable, so at least one of the task was updated therefore we loaded the tasks as well. The hx-swap-oob="true" tells HTMX to swap the element having tblData id.
Basically that's it. Just include an OOB-Swap task list in each response, where the task list needs an update. If you load the "new task form" you don't need it, but if you add/update/delete a task, you need a fresh OOB-Swap task list in the response (fetched from the database after the task operation has been finished).

What do I need to do if I want to use database data conditionally in Django templates?

I am working on an ecommerce store in Django. I want to know that how do I use the database data passed to templates using render() method, conditionally through JavaScript or AJAX or JSON?
For example, let's say I have a following models.py:
from django.db import models
class Suit(models.Model):
title = models.CharField(max_length = 100, verbose_name = "Title")
img = models.FileField(upload_to = 'suit-products/', null = True, verbose_name = "Picture")
def __str__(self):
return f"{self.title}"
class Buttoning(models.Model):
title = models.CharField(max_length = 100, verbose_name = "Title")
img = models.FileField(upload_to = 'buttonings/', null = True, verbose_name = "Picture")
def __str__(self):
return f"{self.title}"
and following views.py
from django.shortcuts import render
def index(request):
suit_prods = Suit.objects.all()
buttoning = Buttoning.objects.all()
context {
"suit_prods": suit_prods,
"buttoning": buttoning
}
return render(request, "index/index.html", context)
and following index.html (template):
{% for element in suit_prods %}
<li>
<a href="#">
<div id="menu">
<img src="{{ element.img.url }}" />
<span>{{ element.title }}</span>
<span></span>
</div>
</a>
</li>
{% endfor %}
Now what I want is, if the clicked element in the list items in index.html has the title as "two_piece_suit" then show items of {{ buttoning }} as a list, otherwise pass.
If I explain it more using some JS syntax, then I want following kind of behaviour:
<scrip>
var suit_menu = document.getElementsByClassName("menu");
for(var i = 0; i < suit_menu.length; i++) {
if(suit_menu.text == "two_piece_suit") {
{% for element in buttoning %}
<li>
<a href="#">
<div id="buttoning">
<img src="{{ element.img.url }}" />
<span>{{ element.title }}</span>
<span></span>
</div>
</a>
</li>
{% endfor %}
}
}
</script>
If I understand correctly, you don't necessarily need JavaScript to achieve this. Also, <script> is missing a t, and your for loop doesn't seem to add the HTML to the document at all.
You can achieve that purely with the Django Template Language by integrating the buttons for loop with an if condition like this:
{% if element.title == 'two_piece_suit' %}
{% for button in buttoning %}
<Some HTML>{{ button.title }}</Some HTML>
{% endfor %}
{% endif %}
That way, the list of buttons is only displayed if the title is two_piece_suit.

search box for all the pages after Django pagination of table

I have developed a web app using Django.
I have created a table pagination.
how could I create a search input for all the data in all the pages of table?
view.py
def Browse_and_adopt(request):
Title_list = Title.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(Title_list, 10)
try:
Titles = paginator.page(page)
except PageNotAnInteger:
Titles = paginator.page(1)
except EmptyPage:
Titles = paginator.page(paginator.num_pages)
page_obj = paginator.get_page(page)
return render(request, 'bms/inbox/Browse_and_adopt.html', {'Titles': Titles, 'page_obj': page_obj})
Browse_and_adopt.html
<table id="myTable">
<thead>
<tr>
<th>Title</th>
</tr>
</thead>
<tbody>
% for book in page_obj %}
<tr>
<td>{{ book.title }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
Now only the first page shows, how to create a search bar for all the data in the table?
The view does not know whick object you are looking for.
There is two ways to do so.
In the frontend end where you write a JS callback event listener that filter all the supplied table data. " not optimal ''
Or you can create a custom view that recieve the object pk via post request and return the filtered result e.g url :
urlpatterns = [
path('update/<int:pk>/', updateViewSet.as_view()),
]

How to pass a modified sortable list from html to another html?

I am writing web application by Flask. I made a sortable list in HTML using jquery-sortable.js. I want to pass the new sequences of the list after making modification from one HTML to another HTML.
I have tried to use GET/POST methods. However, it didn't work. Nothing was passed to another HTML.
home.html
<script>
...
$('#sortedList').val(sorted_list);
...
</script>
<form action="http://localhost:5000/about" method="POST">
{{ form.hidden_tag() }}
<div class="form-group" style="display: none;">
{{ form.sortedList.label(class="form-control-label") }}
{{ form.sortedList(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn_outline-info") }}
</div>
</form>
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class SortedForm(FlaskForm):
sortedList = StringField('Sorted List', validators=[DataRequired()])
submit = SubmitField('Submit')
main.py
#app.route("/about", methods=['GET', 'POST'])
def about():
form = SortedForm()
if form.validate_on_submit():
flash(f'Sorted list passed {form.sortedList.data}~', 'success')
return render_template('about.html', title='About', form=form)
about.html
{% extends "layout.html" %}
{% block content %}
{{ form.sortedList.data }}
{% endblock content %}

load more feature or endless pagination

I recently learnt I need pagination otherwise page won't load faster.
so I implemented it.
But now that I finished implemented, I realize I have some problem.
My web has a format like this
The above is the top part of the page
and the above is the below part of the page.
all my posts are going into the below part, and because more posts I write more posts will be there I implemented pagination.
the pagination works fine, but when I go to the next page the top part remains there while the below part shows new posts. (I have implemented this without realizing the existence of the top part)
I don't want my users to see the top part every time they click next page.
I think I have two ways to solve this problem.
One is to somehow not show the top part when they click next page.
Or
I use load more button to show more posts instead of going into another page.
Problem is I don't know how to do either one of them..can some one please help me?
def category_detail(request, slug):
obj = NewsCategory.objects.get(slug=slug)
newsInCat = obj.news_set.all() #for the list of news
paginator = Paginator(newsInCat, 25) # Show 25 contacts per page
page = request.GET.get('page')
try:
news_set = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
news_set = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
news_set = paginator.page(paginator.num_pages)
bestInCat = obj.news_set.get_bestInCat()
context = {
"obj":obj,
"news_set":news_set,
"newsInCat":newsInCat,
"bestInCat":bestInCat,
}
return render(request, "news/category_detail.html", context)
<div class="row">
<div>
{% for news in news_set %}
<div class='col-sm-4'>
<div class="content">
<figure class="story-image">
</figure>
<div id="forever "style="margin-bottom:30px;">
<a href='{{news.get_absolute_url }}' style="text-decoration:none; color:#282E5C;"><h4 style="font-size: 18px;
font-weight: 400;">{{news.title}}</h4></a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="pagination">
<span class="step-links">
<!-- {% if news_set.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ news_set.number }} of {{ news_set.paginator.num_pages }}.
</span> -->
{% if news_set.has_next %}
Load More
{% endif %}
</span>
</div>
1) in the html you can show top block if page number equals 1. For example
{% if news_set.number==1%}
{{ top_block}}
{% endif %}
<div class="row">
<div>
{% for news in news_set %}
<div class='col-sm-4'> ....
2) you can render partial html if request is ajax
Here is simple code
views.py
def category_detail(request, slug):
obj = NewsCategory.objects.get(slug=slug)
newsInCat = obj.news_set.all() #for the list of news
paginator = Paginator(newsInCat, 25) # Show 25 contacts per page
page = request.GET.get('page')
try:
news_set = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
news_set = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
news_set = paginator.page(paginator.num_pages)
if request.is_ajax():
context = {
'news_set':news_set
}
return render(request,"news/category_detail_ajax.html",context)
else:
bestInCat = obj.news_set.get_bestInCat()
context = {
"obj":obj,
"news_set":news_set,
"newsInCat":newsInCat,
"bestInCat":bestInCat,
}
return render(request, "news/category_detail.html", context)
category_detail_ajax.html
{% for news in news_set %}
<div class='col-sm-4'>
<div class="content">
<figure class="story-image">
</figure>
<div id="forever "style="margin-bottom:30px;">
<a href='{{news.get_absolute_url }}' style="text-decoration:none; color:#282E5C;"><h4 style="font-size: 18px;
font-weight: 400;">{{news.title}}</h4></a>
</div>
</div>
</div>
{% endfor %}
javascript
jQuery(document).on('click','.load-more',function(e){
e.preventDefault();
jQuery.ajax({
url:jQuery(this).attr('href')
}).done(function(data){
jQuery('.row>div').append(data);
});
});

Categories

Resources