Pagination for the objects returned in 'else' clause - django-models

I have following model, where pagination works ok only if I don't filter by tags. As soon as I filter ads by tags, pagination doesn't work.
class AdListView(ListView):
paginate_by = 2
model = Ad
context_object_name = 'ads'
def get_context_data(self, **kwargs):
if self.request.GET.get('tags') is None:
context = super().get_context_data(**kwargs)
context.update(tags=Tag.objects.all())
return context
else:
tag_submitted = Tag.objects.get(name=self.request.GET.get('tags'))
ads_by_tag = tag_submitted.related_ads.all()
context = {
'ads': ads_by_tag
}
context.update(tags=Tag.objects.all())
return context
What needs to be done so that pagination works for both cases, with and without filter?
below are models.py and template
models.py
class Tag(BaseModel):
def __str__(self):
return str(self.name)
class Ad(BaseModel):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
seller = models.ForeignKey(Seller, on_delete=models.CASCADE)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
tags = models.ManyToManyField(Tag, related_name='related_ads')
price = models.PositiveIntegerField(default=0)
archived = models.BooleanField(default=False)
def __str__(self):
return str(self.name)
class Meta:
ordering = ['-date_created']
template
{% block content %}
<div class="container">
<div class="row">
<div class="w-75">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
</tr>
</thead>
<tbody>
{% for ad in ads %}
<tr>
<td>{{ forloop.counter }}</td>
<td> {{ ad.name }} </td>
</tr>
{% empty %}
<li>No ads yet.</li>
{% endfor %}
</tbody>
</table>
</div>
<div class="w-25">
<table class="table">
<thead>
<tr>
<th scope="col">Tags</th>
</tr>
</thead>
<tbody>
{% for tag in tags %}
<tr>
<td> {{ tag }} </td>
</tr>
{% empty %}
<li>No tags yet.</li>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="row">
<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>
</div>
</div>
{% endblock %}
so this is a project to learn django, so any other comments on how to improve the code also welcome.

You should not filter in the .get_context_data() [Django-doc]. In Django you pass the (possibly) filtered queryset in .get_queryset(…) [Django-doc]. Django will then paginate the queryset, and add it to the context. By overriding the item in the context, you now have removed the paginated queryset, and added a filtered queryset instead.
You thus provide the filtered queryset, and let Django do the proper pagination:
class AdListView(ListView):
paginate_by = 2
model = Ad
context_object_name = 'ads'
def get_search_querystring(self):
copy = self.request.GET.copy()
copy.pop(self.page_kwarg, None)
return copy.urlencode()
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if 'tags' in self.request.GET:
return qs.filter(tags__name=self.request.GET['tags'])
return qs
def get_context_data(*args, **kwargs):
return super().get_context_data(*args, tags=Tag.objects.all(), **kwargs)
Another problem is the link in the template: by using ?page={{ page_obj.next_page_number }}, this will "drop" the query string [wiki] that contains the search query, and only use page as the only variable in the query string.
In the template we thus can use the URL as:
<a href="?page={{ page_obj.next_page_number }}&{{ view.get_search_queryset|safe }}">
the same should happen with the other pagination links.

Related

How I can show the models fields in my template

I am trying to extract the data from a model and show it to a template, but the data are not showing. Any help will be appreciated.
Here is the model:
class Schedule(models.Model):
team1 = models.CharField(max_length=50)
team1_pic = models.ImageField()
team2 = models.CharField(max_length=50)
team2_pic = models.ImageField()
timestamp = models.TimeField(default="", blank=True, null=True)
datestamp = models.DateField(default="", blank=True, null=True)
location = models.CharField(max_length=200)
Here is the view:
def schedule(request):
schedule = Schedule.objects.all()
context = {
'schedule': schedule
}
return render(request, 'schedule.html')
And finally the template:
<!-- Schedule Section Begin -->
<section class="schedule-section spad">
<div class="container">
<div class="row">
<div class="col-lg-8 left-blog-pad">
<div class="schedule-text">
<h4 class="st-title">World Cup 2019</h4>
<div class="st-table">
{% for sch in schedule %}
<table>
<tbody>
<tr>
<td class="left-team">
<img src="{% static 'img/schedule/flag-1.jpg' %}" alt="">
<h4>{{ sch.team1 }}</h4>
</td>
<td class="st-option">
<div class="so-text">{{ sch.location }}</div>
<h4>VS</h4>
<div class="so-text">{{ sch.date }}</div>
</td>
<td class="right-team">
<img src="{% static 'img/schedule/flag-2.jpg' %}" alt="">
<h4>{{ sch.team1 }}</h4>
</td>
</tr>
</tbody>
</table>
</div>
{% endfor %}
</div>
</div>
You need to pass context too.
def schedule(request):
schedule = Schedule.objects.all()
context = {
'schedule': schedule
}
return render(request, 'schedule.html',context)

How to add "caption" tag in TableBlock template in StreamField?

I'd like to add <caption> tag for tables created with TableBlock in StreamField, to make the tables more accessible and semantically correct. Right now, the default template doesn't have that tag. I'm not sure how I should be customizing the table.html template (from the default, and I'm not sure how to render the table if I make a custom class for the table block to add captioning.
<caption> must be <table>'s first child, that's why I need to tinker with the template. Anyone has doe this before?
<table>
<caption>Example Caption</caption> <--- Need this here
<thead>
<th>Header col 1</th>
<th>Header col 2</th>
</thead>
<tbody>
<tr>
<td>Cell col 1</td>
<td>Cell col 2</td>
</tr>
</tbody>
</table>
Take a look at the following code.
TableBlock.py
class TableHead(blocks.StructBlock):
table_head_text = blocks.ListBlock(blocks.CharBlock(max_length=120))
class TableRow(blocks.StructBlock):
table_row_text = blocks.ListBlock(blocks.CharBlock(max_length=120))
class TableBlock(blocks.StructBlock):
caption_text = blocks.CharBlock(max_length=120)
table_head = TableHead()
table_rows = blocks.ListBlock(TableRow())
class Meta:
template = 'wagtail_blocks/table.html'
You will notice that I have set the 'template' property in the 'Meta' class, so you will have to create that file too.
table.html
{% load wagtailcore_tags %}
<table>
<caption>{{ value.caption_text }}</caption>
<thead>
{% for header_text in value.table_head.table_head_text %}
<th>{{ header_text }}</th>
{% endfor %}
</thead>
<tbody>
{% for table_row in value.table_rows %}
<tr>
{% for table_row_text in table_row.table_row_text %}
<td>{{ table_row_text }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
Finally you can easily use your new TableBlock in any page like this
body = StreamField([
('table', TableBlock()),
])

Editing the model in a ListView

I'm currently making a website for my parent's bakery. And I want to make a custom admin page for them so they can update their products with ease.
All I have so far is a ListView that displays all the products.
This is my model:
class menulist(models.Model):
name = models.CharField(max_length=120)
description = models.CharField(max_length=500)
price = models.DecimalField(decimal_places=1, max_digits=10, default=100.00)
category_choices = (
('breads', 'Breads'),
('cakes', 'Cakes'),
('hotfood', 'Hot Food'),
('porkrolls', 'Pork Rolls'),
('drinks', 'Drinks'),
('MISC', 'Misc'),
)
category = models.CharField(max_length=50, choices=category_choices, default='MISC',)
dateadded = models.DateField(auto_now_add=True)
dateupdated = models.DateField(auto_now=True)
img = models.ImageField(upload_to='products/', default='products/blank.jpg')
def __str__(self):
return self.name
The View:
class ProductAdminView(ListView):
template_name = 'menulistapp/product_admin.html'
queryset = menulist.objects.all()
The template:
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="row">
<table class="table table-striped table-hover ">
<thead class="thead-dark">
<tr>
<th style="width: 15%"scope="col"></th>
<th style="width: 55%" scope="col">Product Name</th>
<th scope="col">Category</th>
<th scope="col">Price</th>
<th scope="col">Date Added</th>
<th scope="col">Last Updated</th>
</tr>
</thead>
<tbody>
{% for obj in object_list %}
<tr>
<td class="align-middle"><img src="{{ obj.img.url }}" class="img-fluid"></td>
<td class="align-middle">{{ obj.name }}</td>
<td class="align-middle">{{ obj.get_category_display }}</td>
<td class="align-middle">${{ obj.price }}</td>
<td class="align-middle">{{ obj.dateadded }}</td>
<td class="align-middle">{{ obj.dateupdated }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
How do I separate the items into their own category tables?
And how do I make it so that each cell is editable with their own CharField, and there's a "SAVE" button at the bottom of each table?
EG:
You have to use the concept of forms to attain that
For more information, go through https://docs.djangoproject.com/en/2.1/topics/forms/

iterating over length of string variable jinja2

I'm trying to iterate over the length of a string variable in jinja2.
what I have is:
<table>
<tbody>
{% for i in range(string1|length) %}
<tr>
<td>
test
</td>
</tr>
{% endfor %}
</tbody>
</table>
but when I run the application with flask literally nothing is displayed.
what am I doing wrong?

How to send and show messages in AngularJS and Symfony2 project

I'm trying to capture errors inside Symfony2 controller as this code shows:
public function indexAction($parent_id = 0) {
$response['message'] = "";
$breadcrumbs = array();
$response['entities'] = array();
if (!$entity) {
throw $this->createNotFoundException('No se encontraron grupos de detalles');
}
...
return new JsonResponse($response);
}
}
When !$entity is TRUE then as you see I generate a NotFoundException but I need to show a message to users in my AngularJS template. This is the code for the template:
<ul id="nav-info" class="clearfix">
<li><i class="icon-home"></i></li>
<li>Grupo de Meta-Detalles</li>
<li class="active">Listar</li>
</ul>
<h3 class="page-header page-header-top">Grupo de Meta-Detalles <small>Listado.</small></h3>
<table id="example-datatables" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th class="span1"></th>
<th class="span1 hidden-phone">#</th>
<th><i class="icon-bookmark"></i> Nombre</th>
<th><i class="icon-bookmark"></i> Padre</th>
<th><i class="icon-bolt"></i> Descripción</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in MetaDetailGroup">
<td class="span1">
<div class="btn-group">
<i class="icon-pencil"></i>
<i class="icon-remove"></i>
</div>
</td>
<td class="span1 hidden-phone">{% verbatim %}{{ item.id }}{% endverbatim %}</td>
<td>{% verbatim %}{{ item.name }}{% endverbatim %}</td>
<td>{% verbatim %}{{ item.parent }}{% endverbatim %}</td>
<td>{% verbatim %}{{ item.description }}{% endverbatim %}</td>
</tr>
</tbody>
</table>
How I can achieve this? I mean if there is nothing to show (!entity) then show a message to users, any help or advice?
I have no AngularJS installed, so this is checked on JsFiddle :)
public function indexAction($parent_id = 0) {
$response['message'] = "";
$breadcrumbs = array();
$response['entities'] = array();
if (!$entity) {
// Exception is replaced by message to your client-side script
$response['message'] = 'No se encontraron grupos de detalles';
}
return new JsonResponse($response);
}
Make call to your symfony method (GET or POST):
$http.get("/api/index/?parent_id=666", {})
.success(function(data, status, headers, config){
// Would be nice to check status/message and to hide if no error
$scope.ajaxResponse = data.message;
// TODO: Handle your entities
}).error(function(data, status, headers, config){
$scope.ajaxResponse = status;
});
In your view page (HTML) insert some Angular JS variable
<tbody>
<tr><td colspan="5">{{ajaxResponse}}</td></tr>
<tr ng-repeat="item in MetaDetailGroup">
...

Resources