pagination shows same results on every page in django - django

I have the following view that gets and paginates search results:
def search(request):
query=request.GET.get('q','')
form=SearchForm({'q': query })
results = form.search()
paginator = Paginator(results, 15)
print 'page is: '+request.GET.get('page')
try:
page = paginator.page(int(request.GET.get('page')))
except InvalidPage:
raise Http404("No such page of results!")
for result in page.object_list:
print result.object.name
#this prints the same every time!
context = {
'view':resolve(request.path_info).url_name,
'form':form,
'page':page,
'query':query,
}
return render(request, 'nutrition/food-log.html', context)
My template looks like the following:
{% if query %}
<h3>Results</h3>
{% for result in page.object_list %}
<p>{{ result.object.name }}</p>
{% empty %}
<p>No results found.</p>
{% endfor %}
{% if page.has_previous or page.has_next %}
<div>
{% if page.has_previous %}{% endif %}« Previous{% if page.has_previous %}{% endif %}
|
{% if page.has_next %}{% endif %}Next »{% if page.has_next %}{% endif %}
</div>
{% endif %}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
Does anyone know why this is happening?

Related

Django admin template - grid instead of list

What is the easiest / fastest way to override Django admin form to display grid of elements instead of list? My model includes a field for a 100x100px pic, and it would be easier to see them as a grid of pictures, not a list.
This is not terribly difficult, but it requires you to copy several large chunks of code from the Django library and incorporate them into your own application. Here's the overview:
You'll need to copy and then modify several template tags from django.contrib.admin.templatetags.admin_list:
django.contrib.admin.templatetags.admin_list.items_for_result
django.contrib.admin.templatetags.admin_list.result_list
django.contrib.admin.templatetags.admin_list.results
django.contrib.admin.templatetags.admin_list.result_list_tag
You'll need to copy and then modify a couple of templates:
django/contrib/admin/templates/admin/change_list.html
django/contrib/admin/templates/admin/change_list_results.html
For purposes of illustrating the solution, I have created an app called spam and a model called SpamPhoto, along with a model admin that is customized to display the image tag. I am using Bootstrap 4 for the grid. There are still some issues you might need to track down and figure out, like getting rid of the checkbox that shows up for each item. But this code should get about 90% of the way there.
Here's an overview of the files that I created/modified, and which you'll find below:
spam/models.py: contains the SpamPhoto model
spam/admin.py: contains the SpamPhotoAdmin
spam/templatetags/spamphoto_admin_list.py: contains the template tag / functions that will render the customized change list
spam/templates/admin/spam/spamphoto/change_list.html: this is the main changelist template, and only has a few small changes: it loads our custom template tags, it includes Bootstrap CSS, and it calls a customized template tag (defined below)
spam/templates/admin/spam/spamphoto/change_list_results.html: heavy modifications here, getting rid of the table elements and replacing with divs.
So again, this is a LOT of code, and most of it is lifted directly without very many changes. Here we go:
# spam/models.py
from django.db import models
class SpamPhoto(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField()
# spam/admin.py
from django.contrib import admin
from django.utils.html import mark_safe
class SpamPhotoAdmin(admin.ModelAdmin):
list_display = ('name', 'image_tag')
list_display_links = ('name', 'image_tag')
def image_tag(self, obj):
return mark_safe(f'<img class="img-fluid" src="{obj.image.url}" />')
# spam/templatetags/spamphoto_admin_list.py
from django import template
# Note: you should import these from their "correct" locations! This is cheap/dirty:
from django.contrib.admin.templatetags.admin_list import (ResultList, result_headers, result_hidden_fields,
_coerce_field_name, lookup_field, ObjectDoesNotExist,
display_for_field, datetime, display_for_value, models,
mark_safe, NoReverseMatch, add_preserved_filters, format_html)
from django.contrib.admin.templatetags.base import InclusionAdminNode
register = template.Library()
def spamphoto_items_for_result(cl, result, form):
"""
Cloned from django.contrib.admin.templatetags.admin_list.items_for_result
The only modification is found at the very end of this function, where the HTML
tags are changed from `td` to `div`
"""
def link_in_col(is_first, field_name, cl):
if cl.list_display_links is None:
return False
if is_first and not cl.list_display_links:
return True
return field_name in cl.list_display_links
first = True
pk = cl.lookup_opts.pk.attname
for field_index, field_name in enumerate(cl.list_display):
empty_value_display = cl.model_admin.get_empty_value_display()
row_classes = ['field-%s' % _coerce_field_name(field_name, field_index)]
try:
f, attr, value = lookup_field(field_name, result, cl.model_admin)
except ObjectDoesNotExist:
result_repr = empty_value_display
else:
empty_value_display = getattr(attr, 'empty_value_display', empty_value_display)
if f is None or f.auto_created:
if field_name == 'action_checkbox':
row_classes = ['action-checkbox']
boolean = getattr(attr, 'boolean', False)
result_repr = display_for_value(value, empty_value_display, boolean)
if isinstance(value, (datetime.date, datetime.time)):
row_classes.append('nowrap')
else:
if isinstance(f.remote_field, models.ManyToOneRel):
field_val = getattr(result, f.name)
if field_val is None:
result_repr = empty_value_display
else:
result_repr = field_val
else:
result_repr = display_for_field(value, f, empty_value_display)
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
row_classes.append('nowrap')
row_class = mark_safe(' class="%s"' % ' '.join(row_classes))
# If list_display_links not defined, add the link tag to the first field
if link_in_col(first, field_name, cl):
table_tag = 'th' if first else 'td'
first = False
# Display link to the result's change_view if the url exists, else
# display just the result's representation.
try:
url = cl.url_for_result(result)
except NoReverseMatch:
link_or_text = result_repr
else:
url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url)
# Convert the pk to something that can be used in Javascript.
# Problem cases are non-ASCII strings.
if cl.to_field:
attr = str(cl.to_field)
else:
attr = pk
value = result.serializable_value(attr)
link_or_text = format_html(
'<a href="{}"{}>{}</a>',
url,
format_html(
' data-popup-opener="{}"', value
) if cl.is_popup else '',
result_repr)
yield format_html('<{}{}>{}</{}>', table_tag, row_class, link_or_text, table_tag)
else:
# By default the fields come from ModelAdmin.list_editable, but if we pull
# the fields out of the form instead of list_editable custom admins
# can provide fields on a per request basis
if (form and field_name in form.fields and not (
field_name == cl.model._meta.pk.name and
form[cl.model._meta.pk.name].is_hidden)):
bf = form[field_name]
result_repr = mark_safe(str(bf.errors) + str(bf))
# THIS LINE HAS BEEN CHANGED:
yield format_html('<div{}>{}</div>', row_class, result_repr)
if form and not form[cl.model._meta.pk.name].is_hidden:
# THIS LINE HAS BEEN CHANGED:
yield format_html('<div>{}</div>', form[cl.model._meta.pk.name])
def spamphoto_result_list(cl):
"""
Cloned from django.contrib.admin.templatetags.admin_list.result_list
The only change is to the `results` value in the returned dict, where we call `spamphoto_results`
"""
headers = list(result_headers(cl))
num_sorted_fields = 0
for h in headers:
if h['sortable'] and h['sorted']:
num_sorted_fields += 1
return {
'cl': cl,
'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': headers,
'num_sorted_fields': num_sorted_fields,
# THIS LINE HAS BEEN CHANGED:
'results': list(spamphoto_results(cl)),
}
def spamphoto_results(cl):
"""
Cloned from django.contrib.admin.templatetags.admin_list.results
The only changes are where we call `spamphoto_items_for_result` instead of `items_for_result`
"""
if cl.formset:
for res, form in zip(cl.result_list, cl.formset.forms):
# THIS LINE HAS BEEN CHANGED:
yield ResultList(form, spamphoto_items_for_result(cl, res, form))
else:
for res in cl.result_list:
# THIS LINE HAS BEEN CHANGED:
yield ResultList(None, spamphoto_items_for_result(cl, res, None))
#register.tag(name='spamphoto_result_list')
def spamphoto_result_list_tag(parser, token):
"""
Cloned from django.contrib.admin.templatetags.admin_list.result_list_tag
The only change is to the `func` param, which now uses out custom `spamphoto_result_list` function
"""
return InclusionAdminNode(
parser, token,
# THIS LINE HAS BEEN CHANGED:
func=spamphoto_result_list,
template_name='change_list_results.html',
takes_context=False,
)
# spam/templates/admin/spam/spamphoto/change_list.html
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_list %}
<!-- ADDED THIS LINE / INCLUDING OUR CUSTOM TEMPLATE TAGS -->
{% load spamphoto_admin_list %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}">
{% if cl.formset %}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">
{% endif %}
{% if cl.formset or action_form %}
<script src="{% url 'admin:jsi18n' %}"></script>
{% endif %}
{{ media.css }}
{% if not actions_on_top and not actions_on_bottom %}
<style>
#changelist table thead th:first-child {width: inherit}
</style>
{% endif %}
<!-- ADDED THIS LINE / INCLUDING BOOTSTRAP -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.2/dist/css/bootstrap.min.css">
{% endblock %}
{% block extrahead %}
{{ block.super }}
{{ media.js }}
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
{% translate 'Home' %}
› {{ cl.opts.app_config.verbose_name }}
› {{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}
{% endif %}
{% block coltype %}{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
<ul class="object-tools">
{% block object-tools-items %}
{% change_list_object_tools %}
{% endblock %}
</ul>
{% endblock %}
{% if cl.formset and cl.formset.errors %}
<p class="errornote">
{% if cl.formset.total_error_count == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
</p>
{{ cl.formset.non_form_errors }}
{% endif %}
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
<div class="changelist-form-container">
{% block search %}{% search_form cl %}{% endblock %}
{% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
<form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>{% csrf_token %}
{% if cl.formset %}
<div>{{ cl.formset.management_form }}</div>
{% endif %}
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
<!-- THIS LINE HAS BEEN CHANGED: -->
{% spamphoto_result_list cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}
{% block pagination %}{% pagination cl %}{% endblock %}
</form>
</div>
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% translate 'Filter' %}</h2>
{% if cl.has_active_filters %}<h3 id="changelist-filter-clear">
✖ {% translate "Clear all filters" %}
</h3>{% endif %}
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
</div>
{% endif %}
{% endblock %}
</div>
</div>
{% endblock %}
# spam/templates/admin/spam/spamphoto/change_list_results.html
{% load i18n static %}
{% if result_hidden_fields %}
<div class="hiddenfields">{# DIV for HTML validation #}
{% for item in result_hidden_fields %}{{ item }}{% endfor %}
</div>
{% endif %}
{% if results %}
<div class="container">
<div class="row">
{% for result in results %}
{% if result.form and result.form.non_field_errors %}
<div>{{ result.form.non_field_errors }}</div>
{% endif %}
<div class="col-sm-2">{% for item in result %}{{ item }}{% endfor %}</div>
{% endfor %}
</div>
</div>
{% endif %}

Overriding django admin pagination along with url parameters

I would like to implement custom pagination for my admin panel.
My url looks like the following:
http://localhost:8000/admin/items/?group_id=20
On this URL I do some work to filter the results using the parameter group_id (by overriding get_changelist method).
The page results are corrects, the problem is my pagination ending up like this http://localhost:8000/admin/items/?p=1 whereas I would like the URL to be http://localhost:8000/admin/items/?group_id=20&p=1 and keep the parameter.
Basically I want the same result as How to paginate Django with other get variables? but using Django admin.
How can I keep the parameter along with the pagination?
I've tried overriding pagination.html file but without any success.
Thank you.
Edit
I've tried overriding pagination.html but request.GET.items is still empty (even if my settings file is well configured)
{% load admin_list %}
{% load i18n %}
{% load content_extras %}
<p class="paginator">
{% if pagination_required %}
{% for i in page_range %}
{{ i }}
{% endfor %}
{% endif %}
{{ cl.result_count }} {% if cl.result_count == 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endif %}
{% if show_all_url %}{% trans 'Show all' %}{% endif %}
{% if cl.formset and cl.result_count %}<input type="submit" name="_save" class="default" value="{% trans 'Save' %}">{% endif %}
</p>
FOUND A SOLUTION:
1/ Override changelist_view on admin.py and pass the extra data
def changelist_view(self, request, extra_context=""):
response = super(ItemAdmin, self).changelist_view(request, extra_context)
group_id = request.GET.get('group_id', None)
if group_id:
extra_context = {
'group_id': group_id,
}
response.context_data.update(extra_context)
return TemplateResponse(request, "admin/changelist.html", response.context_data)
2/ Create changelist.html file based on django admin template (copy/paste)
Add {% load content_extras %} at the top of the file (line 3)
Change line {% block pagination %}{% pagination cl %}{% endblock %} with {% block pagination %}{% custom_pagination cl %}{% endblock %}
3/ Create content_extras.py under templatetags folder and write custom_pagination function
from django import template
from django.contrib.admin.templatetags import admin_list
register = template.Library()
#register.inclusion_tag('admin/pagination.html', takes_context=True)
def custom_pagination(context, cl):
pagination = admin_list.pagination(cl)
if 'group_id' in context:
params = (('group_id', context['group_id']),)
pagination['params'] = params
return pagination
4/ Create pagination.html (same location as changelist.html)
{% load admin_list %}
{% load i18n %}
{% load content_extras %}
<p class="paginator">
{% if pagination_required %}
{% for i in page_range %}
{{ i }}
{% endfor %}
{% endif %}
{{ cl.result_count }} {% if cl.result_count == 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endif %}
{% if show_all_url %}{% trans 'Show all' %}{% endif %}
{% if cl.formset and cl.result_count %}<input type="submit" name="_save" class="default" value="{% trans 'Save' %}">{% endif %}
</p>
If you activated django.template.context_processors.request in your settings, you can access parameters from your request directly from your template.
And then you can access the parameters in your templates directly. Something like:
href="?page={{ data.next_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}"
Research (context_processors.request was moved from django.core to django.templates some time ago)

How to make django-rules work in templates?

When using Django-Rules, you can check permissions in views. But mine always returns false. Code in example below always displays 'bar', but must display 'foo':
{% extends 'home/base.html' %}
{% load rules %}
{% has_perm 'anApp.can_access_something' user as can_access_something %}
{% block content %}
{% if can_access_something %}
<p>foo</p>
{% else %}
<p>bar</p>
{% endif %}
...
{% endblock %}
My rules.py looks like this:
#rules.predicate
def testrule(user):
return True
rules.add_perm('anApp.can_access_something', testrule)
So will always return true.
The following line must be in a block:
{% has_perm 'anApp.can_access_something' user as can_access_something %}
This will correctly display 'foo':
{% extends 'home/base.html' %}
{% load rules %}
{% block content %}
{% has_perm 'anApp.can_access_something' user as can_access_something %}
{% if can_access_something %}
<p>something</p>
{% else %}
<p>something else</p>
{% endif %}
...
{% endblock %}

How I can get User.username and Group.name

I need to get the username and the name of group for all user.
The model that I use are User(default) and Group(default)
index.html:
{% for dato in usuarios %}
{{dato.first_name|title}}
{% for item in jefes %}
{{dato.groups}}
{% if dato.groups == item.id %} #I don't know to do this if
{{item.name}}
{% endif %}
{% endfor %}
{% endfor %}
view:
def inicio(request):
usuarios = User.Group.all()
grupos = Group.objects.all()
ctx = {'usuarios':usuarios,'grupos':grupos}
return render_to_response('index.html', ctx, context_instance=RequestContext(request))
You do not need the groups queryset from the view.
{% for dato in usuarios %}
{{dato.first_name|title}}
{% for group in dato.groups.all %}
{{group.name}}
{% endfor %}
{% endfor %}

How can I get a variable passed into an included template in django

I am a Django newbie and am unable to achieve something trivial. Please help me with this.
I am setting a variable pgurl in my views.py
Am able to access the variable {{pgurl}} in my with_tag.html template. This template includes a pagination.html template into itself
In pagination.html I am unable to use the variable {{pgurl}} and nothing is printed
How can I get this variable passed into the included template?
views.py
def with_tag(request, tag, template_name='main/with_tag.html', current_page=1, pgurl=''):
if request.method == 'GET':
query_tag = Tag.objects.get(name=tag)
primes = TaggedItem.objects.get_by_model(Prime, query_tag)
primes = primes.order_by('-date')
request.page = current_page
tcm_pp = TCM_ITEMS_PER_PAGE
pgurl = request.path
else:
return HttpResponseRedirect(request.path)
return direct_to_template(request, template_name, { 'primes' : primes, 'prime_total' : Prime.objects.count(), 'now': datetime.now(), 'page' : current_page, 'tcm_pp' : tcm_pp, 'tag' : tag, 'pgurl' : pgurl })
with_tag.html
{% extends "base.html" %}
{% load comments %}
{% load pagination_tags %}
...
{% include "pagination.html" %}
{% paginate %}
pagination.html
{% if is_paginated %}
{% load i18n %}
<div class="pagination">
{% if page_obj.has_previous %}
‹‹ {% trans "previous" %}
{% else %}
<span class="disabled prev">‹‹ {% trans "previous" %}</span>
{% endif %}
{% for page in pages %}
{% if page %}
{% ifequal page page_obj.number %}
<span class="current page">{{ page }}</span>
{% else %}
{{ page }}
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
{% trans "next" %} ››
{% else %}
<span class="disabled next">{% trans "next" %} ››</span>
{% endif %}
</div>
{% endif %}
It will be helpful if you post the output of the rendered page. The context should get passed, might be your template tags instead. Try to do assert and check to see if the variables were passed correctly.