I am using Flask, mongoengine for a project and I am trying to get basic stuff working from http://docs.mongodb.org/manual/tutorial/write-a-tumblelog-application-with-flask-mongoengine/
After implementing everything from above link I added a new field for "tags" in Post and when I try to create a post, my tags doesn't show a input box.
Any help is appreciated.
My code below
class Post(db.Document):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
slug = db.StringField(max_length=255, required=True)
body = db.StringField(required=True)
views = db.IntField(default=0)
category = db.StringField()
tags = db.ListField(db.StringField(max_length=30))
template
{% macro render(form) -%}
<fieldset>
{% for field in form %}
{% if field.type in ['CSRFTokenField', 'HiddenField'] %}
{{ field() }}
{% else %}
<div class="clearfix {% if field.errors %}error{% endif %}">
{{ field.label }}
<div class="input">
{% if field.type == 'ListField' %}
{% for subfield in field.entires %}
{% if subfield.type == 'StringField' %}
{{ field }}
{% endif %}
{% endfor %}
{% elif field.name == "body" %}
{{ field(rows=10, style="width:360px;") }}
{% else %}
{{ field() }}
{% endif %}
{% if field.errors or field.help_text %}
<span class="help-inline">
{% if field.errors %}
{{ field.errors|join(' ') }}
{% else %}
{{ field.help_text }}
{% endif %}
</span>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</fieldset>
{% endmacro %}
admin.py
from flask import Blueprint, request, redirect, render_template, url_for
from flask.views import MethodView
from pprint import pprint
from flask.ext.mongoengine.wtf import model_form
from tumblog.auth import requires_auth
from tumblog.models import Post
admin = Blueprint('admin', __name__, template_folder='templates')
class List(MethodView):
decorators = [requires_auth]
cls = Post
def get(self,slug=None):
posts = self.cls.objects.all()
return render_template('admin/list.html', posts=posts)
class delList(MethodView):
decorators = [requires_auth]
cls = Post
def get(self,slug=None):
try:
post = self.cls.objects.get(slug=slug)
post.delete()
except:
pass
return redirect(url_for('admin.index'))
class Detail(MethodView):
decorators = [requires_auth]
def get_context(self, slug=None):
form_cls = model_form(Post, exclude=('created_at', 'views'))
if slug:
post = Post.objects.get_or_404(slug=slug)
if request.method == 'POST':
form = form_cls(request.form, inital=post._data)
else:
form = form_cls(obj=post)
else:
post = Post()
form = form_cls(request.form)
context = {
"post": post,
"form": form,
"create": slug is None
}
return context
def get(self, slug):
context = self.get_context(slug)
return render_template('admin/detail.html', **context)
def post(self, slug):
context = self.get_context(slug)
form = context.get('form')
if form.validate():
post = context.get('post')
form.populate_obj(post)
post.save()
return redirect(url_for('admin.index'))
return render_template('admin/detail.html', **context)
# Register the urls
admin.add_url_rule('/admin/', view_func=List.as_view('index'))
admin.add_url_rule('/admin/create/', defaults={'slug': None}, view_func=Detail.as_view('create'))
admin.add_url_rule('/admin/edit/<slug>/', view_func=Detail.as_view('edit'))
admin.add_url_rule('/admin/delete/<slug>/', view_func=delList.as_view('delete'))
Because list fields may have their own special types for each item, you may need to define custom fields; or you can try to use wtforms.fields.FieldList directly.
It is difficult to say which error you are hitting; you can try to put information into the form for debugging, e.g.
{% for field in form %}
<!-- {{field.type}} {{field.label}} -->
... etc ...
{% endfor %}
Also noticed you had a typo
{% for subfield in field.entires %} <=== did you mean field.entries ?
bounty question:
Unless you define help_text when declaring your field, this will not work as expected:
{{ field.help_text }}
You need to include it when declaring your fields, e.g.
oid = ObjectIdField(..., help_text="object id")
Related
Whenever I click on the profiles(links) on the profile_list page, I get the profile doesn't exist error
it always goes to profiles that do not longer exist in the database.
Models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class Profile(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
follows=models.ManyToManyField(
"self",
related_name="followed_by",
symmetrical=False,
blank=True)
def __str__(self):
return self.user.username
#receiver(post_save,sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
user_profile=Profile(user=instance)
user_profile.save()
user_profile.follows.add(instance.profile)
user_profile.save()
admin.py:
from django.contrib import admin
from django.contrib.auth.models import Group,User
from .models import Profile
class ProfileInline(admin.StackedInline):
model=Profile
class UserAdmin(admin.ModelAdmin):
model=User
fields=["username"]
inlines=[ProfileInline]
# Register your models here.
admin.site.unregister(Group)
admin.site.unregister(User)
admin.site.register(User,UserAdmin)
views.py:
from django.urls import reverse
from django.shortcuts import render
from requests import request
from users.models import Profile
from django.http import HttpResponseRedirect
# Create your views here.
def dashboard(request):
return render(request,"inbox/layout.html")
def feed(request):
return render(request,"inbox/feed.html")
def outbox(request):
return render(request,"inbox/outbox.html")
def profile_list(request):
profiles=Profile.objects.exclude(user=request.user)
return render(request,"inbox/profile_list.html",{"profiles":profiles})
def profile(request, pk):
if not hasattr(request.user, 'profile'):
missing_profile = Profile(user=request.user)
missing_profile.save()
profile = Profile.objects.get(pk=pk)
if request.method == "POST":
current_user_profile = request.user.profile
data = request.POST
action = data.get("follow")
if action == "follow":
current_user_profile.follows.add(profile)
elif action == "unfollow":
current_user_profile.follows.remove(profile)
current_user_profile.save()
return render(request, "inbox/profile.html", {"profile": profile})
profile.html:
<!-- inbox/templates/inbox/profile_list.html -->
{% extends 'inbox/layout.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
<div >
<p>#{{ profile.user.username|upper }}</p>
<div class="buttons has-addons">
{% if profile in user.profile.follows.all %}
<button class="button is-success is-static">Follow</button>
<button class="button is-danger" name="follow" value="unfollow">
Unfollow
</button>
{% else %}
<button class="button is-success" name="follow" value="follow">
Follow
</button>
<button class="button is-danger is-static">Unfollow</button>
{% endif %}
</div>
<p1>following:</p1>
<ul>
{% for following in profile.follows.all %}
<li>{{ following }}</li>
{% endfor %}
</ul>
<p2>followers:</p2>
<ul>
{% for follower in profile.followed_by.all %}
<li>{{ follower }}</li>
{% endfor %}
</ul>
</div>
</form>
{% endblock content %}
profile_list.html template:
<!-- inbox/templates/inbox/profile_list.html -->
{% extends 'inbox/layout.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
{% for profile in profiles %}
<div class="column">
<p>{{ profile.user.username }}</p>
<p>#{{ profile.user.username|lower }}</p>
</div>
{% endfor %}
</form>
{% endblock content %}
You need to match Profile's pk with profile = Profile.objects.get(pk=pk), currently you mentioned the pk of User model, which matched the given queryset in profile view, so try sending pk of profile in profile_list.html template:
{% for profile in profiles %}
<div class="column">
<p>{{ profile.user.username }}</p>
<p>#{{ profile.user.username|lower }}</p>
</div>
{% endfor %}
Also, it is better to use get_object_or_404 instead of get, as it calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
So, views.py:
def profile(request, pk):
if not hasattr(request.user, 'profile'):
missing_profile = Profile(user=request.user)
missing_profile.save()
profile = get_object_or_404(Profile, pk=pk)
if request.method == "POST":
current_user_profile = request.user.profile
data = request.POST
action = data.get("follow")
if action == "follow":
current_user_profile.follows.add(profile)
elif action == "unfollow":
current_user_profile.follows.remove(profile)
current_user_profile.save()
return render(request, "home/profile.html", {"profile": profile})
Now, if the profile does not exist, it will show no Profile matches the given query.
I cannot seem to find a way to iterate through the files uploaded by the formset, although the files are uploaded as the code indicates.
When I load the page it shows 1-3 "img src=(unkown)" tags. It shows 3 when I upload 2 images and 1 when I upload 1 image.
Template view-post:
{% block body %}
<div class="container">
<div class="col-md-8">
<p>
<h1>{{ post.title }}</h1>
<h4>{{ post.body }}</h4>
<p>
Posted by {{ post.user }} on {{ post.created_date }}
{% if not post.created_date == post.updated_date %}
and updated on {{ post.updated_date }}
{% endif %}
</p>
</p>
{% for img in images %}
<img src="{{ img.url }}" width=360>
{{ img.url }}
<br>
{% endfor %}
</div>
<div class="col-4-md">
{% if post.cover_image %}
<img src="{{ post.cover_image.url }}" width=240>
{% endif %}
</div>
</div>
{% endblock %}
Here I use filter, not get, so it should give me a list of the images connected via foreignkey to the right post. The post here is displayed as it should be, along with the internal cover image and text in the post.
View view-post:
class ViewPost(TemplateView):
template_name = 'posts/view_post.html'
context_object_name = 'view_post'
def get(self, request, pk):
post = Post.objects.get(pk=pk)
images = Images.objects.filter(post=post)
args = {'post': post, 'images': images}
return render(request, self.template_name, args)
Template make-post:
{% block body %}
<div class="container">
<div class="col-md-8">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form.errors }}
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<button type="submit">Submit</button>
</form>
</div>
</div>
{% endblock %}
View make-post:
class MakePost(DetailView):
template_name = 'posts/make_post.html'
ImageFormSet = modelformset_factory(Images, form=ImageForm, extra=3)
def get(self, request):
form = PostForm()
formset = self.ImageFormSet(queryset=Images.objects.none())
args = {'form': form, 'formset': formset}
return render(request, self.template_name, args)
def post(self, request):
form = PostForm(request.POST, request.FILES)
formset = self.ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.user = request.user
post.cover_image = form.cleaned_data['cover_image']
post.title = form.cleaned_data['title']
post.body = form.cleaned_data['body']
post.save()
for imgform in formset:
image = imgform.cleaned_data.get('image')
photo = Images(post=post, post_image=image)
photo.save()
return redirect('home_space:home')
else:
return redirect('home_space:home')
Function to name files and image Model:
def get_image_filename(instance, filename):
title = instance.post.title
slug = slugify(title)
return "post_images/%s-%s" % (slug, filename)
class Images(models.Model):
post = models.ForeignKey(Post, default=None, on_delete=models.CASCADE)
post_image = models.ImageField(upload_to=get_image_filename,
verbose_name='Image')
In your template, you are accessing url of Images model instance. But, the url property is in ImageField class instance. Use img.post_image.url.
Example:
{% for img in images %}
<img src="{{ img.post_image.url }}" width=360>
{{ img.post_image.url }}
<br>
{% endfor %}
I am new to Django and don't understand what really is causing this:
I have a Model Company which has an OneToOneField, creator.
# models.py
class Company(models.Model):
class Meta:
verbose_name = 'Company'
verbose_name_plural = 'Companies'
creator = models.OneToOneField(User, related_name="company", on_delete=models.CASCADE, unique=False, null=True)
name = models.CharField(max_length=50)
I have a TemplateView class to handle get and post requests for creating a Company model:
# views.py
class create_company(TemplateView):
def get(self, request):
form = CompanyCreateForm()
title = "Some form"
return render(request, "form.html", {"form": form, "title": title})
def post(self, request):
form = CompanyCreateForm(request.POST)
if form.is_valid():
comp = form.save(commit=False)
comp.creator = request.user
comp.save()
return redirect('index')
The form is showing correctly also storing when I submit, the problem I am facing is with base.html where I show {% user.company %}; the form template extends it like:
{% extends "account/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post" action="">
{% csrf_token %}
{{form|crispy}}
<button class="btn btn-success" type="submit">Save</button>
</form>
<br>
</div>
<br>
{% endblock %}
and in base.html I access
{% if user.is_authenticated %}
{% user.company %}
{% endif %}
But user.company is not showing even it is set; it shows only when I redirect to index but not when I render the form.
Can someone help me understand what causes this?
{% if request.user.is_authenticated %}
{% request.user.company %}
{% endif %}
you are not sending any context to the base.html, thus only user wont work.
This was the error when I simulated your code.
Error during template rendering
In template /home/user/django/drf_tutorial/snippets/templates/base.html, error at line 2
Invalid block tag on line 2: 'user.company', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?
1 {% if user.is_authenticated %}
2 {% user.company %}
3 {% endif %}
4 {% block content %}{% endblock %}
It gives hint that the code to show company should be variable {{ }} instead of tag {% %}. So the base.html template should be as below.
{% if user.is_authenticated %}
{{ user.company }}
{% endif %}
{% block content %}{% endblock %}
i have an app called reviews
reviews/forms.py
from django.forms import ModelForm, Textarea
from reviews.models import Review
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['rating', 'comment']
widgets = {
'comment': Textarea(attrs={'cols': 40, 'rows': 15}),
}
reviews/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from .models import Review, Wine
from .forms import ReviewForm
import datetime
from django.contrib.auth.decorators import login_required
#login_required
def add_review(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
form = ReviewForm(request.POST)
if form.is_valid():
rating = form.cleaned_data['rating']
comment = form.cleaned_data['comment']
user_name = form.cleaned_data['user_name']
user_name = request.user.username
review = Review()
review.wine = wine
review.user_name = user_name
review.rating = rating
review.comment = comment
review.pub_date = datetime.datetime.now()
review.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,)))
return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})
reviews/templates/reviews/wine_detail.html
{% extends 'base.html' %}
{% load bootstrap3 %}
{% block title %}
<h2>{{ wine.name }}</h2>
<h5>{{ wine.review_set.count }} reviews ({{ wine.average_rating | floatformat }} average rating)</h5>
{% endblock %}
{% block content %}
<h3>Recent reviews</h3>
{% if wine.review_set.all %}
<div class="row">
{% for review in wine.review_set.all %}
<div class="col-xs-6 col-lg-4">
<em>{{ review.comment }}</em>
<h6>Rated {{ review.rating }} of 5 by {{ review.user_name }}</h6>
<h5><a href="{% url 'reviews:review_detail' review.id %}">
Read more
</a></h5>
</div>
{% endfor %}
</div>
{% else %}
<p>No reviews for this wine yet</p>
{% endif %}
<h3>Add your review</h3>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'reviews:add_review' wine.id %}" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "star" %} Add
</button>
{% endbuttons %}
</form>
{% endblock %}
base.html
{% load bootstrap3 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
{% block bootstrap3_content %}
<div class="container">
<nav class="navbar navbar-default">
<div class="navbar-header">
<a class="navbar-brand" href="{% url 'reviews:review_list' %}">Winerama</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>Wine list</li>
<li>Home</li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li>Hello {{ user.username }}</li>
<li>Logout</li>
{% else %}
<li>Login</li>
<li>Register</li>
{% endif %}
</ul>
</div>
</nav>
<h1>{% block title %}(no title){% endblock %}</h1>
{% bootstrap_messages %}
{% block content %}(no content){% endblock %}
</div>
{% endblock %}
I am getting the error at the line {% bootstrap_form form layout='inline' %} in the html file
Any idea how to fix this?
There's a few problems with your code as it stands, so I'll try to clean it up with some comments as I would write it to add a review to a wine.
#login_required
def add_review(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
if request.POST:
form = ReviewForm(request.POST)
else:
form = ReviewForm()
if form.is_valid():
### NO NEED FOR - already set as part of valid modelform ::: rating = form.cleaned_data['rating']
### AS WELL AS ::: comment = form.cleaned_data['comment']
### THIS IS NOT A FIELD IN YOUR FORM :::user_name = form.cleaned_data['user_name']
user_name = request.user.username
review = form.save(commit=False) # commit = False means that this instantiate but not save a Review model object
review.wine = wine
review.user_name = user_name # Why use this instead of a ForeignKey to user?
review.pub_date = datetime.datetime.now() # works as long as pub_date is a DateTimeField
review.save() # save to the DB now
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,))) # THIS will redirect only upon form save
return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})
Now, the error your seeing is most likely related to you passing request.POST to a form even if request.POST is blank; the form will attempt to set initial values but with a querydict that has no values that actually relates to the form.
EDIT: In response to your comments, my next step would be to try and render each form field individually and see if I can trigger a failure.
Instead of {% bootstrap_form form layout='inline' %}, try-
{% for field in form %}
{% bootstrap_field field %}
{% endfor %}
If this is an error with the django-bootstrap library trying to render the textarea widget and the inline style together (as I would suspect at this point), you can also eliminate the widget parameter and see if there's a fix. If there is, I'd suggest overriding your modelform's init method for assign a widget post a call super on init.
In Class Base View
This error may occur when you use form_class in the wrong generic view.
⮕ Open your views.py then check to see if you have set the wrong generic view in your class.
Example
class ProfileUpdateView(T̶e̶m̶p̶l̶a̶t̶e̶V̶i̶e̶w̶ UpdateView):
model = User
form_class = forms.ProfileForm
success_url = reverse_lazy("stories:story_list")
template_name = 'profile.html'
def get_object(self, queryset=None):
return get_object_or_404(User, pk=self.request.user.id)
I've been using django built-in pagination (is_paginated) in few of my pages. They are all working fine. Except for the search page where the pagination should only appear based on the filtered queryset.
I've checked through few other thread but it ain't helping much.
How do I use pagination with Django class based generic ListViews?
Django template tag exception
Here's a mini version of what I have so far:-
1)views.py
class SearchBookView(ListView):
template_name = 'books/search_book.html'
paginate_by = '2'
context_object_name = 'book'
form_class = SearchBookForm
def get(self, request):
form = self.form_class(request.GET or None)
if form.is_valid():
filtered_books = self.get_queryset(form)
context = {
'form' : form,
'book' : filtered_books,
}
else:
context = {'form': form}
return render(request, self.template_name, context)
def get_queryset(self, form):
filtered_books = Book.objects.all()
if form.cleaned_data['title'] != "":
filtered_books = filtered_books.filter(
title__icontains=form.cleaned_data['title'])
return filtered_books
def get_context_data(self):
context = super(SearchBookView, self).get_context_data()
return context
2) search_book.html (template)
{% crispy form %}
{% if book %}
<p>Found {{ book|length }} book{{ book|pluralize }}.</p>
{% for book in book %}
<div class="card">
<div style="height:170px; border:solid #111111;" class="col-md-3">
Ima
</div>
<div class="whole-card col-md-9">
<div class="title">"{{ book.title }}"</div>
<div>{{ book.description }}</div>
Read More
</div>
</div>
{% endfor %}
{% else %}
<p>No book matched your searching criteria.</p>
{% endif %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
{% endif %}
forms.py
class SearchBookForm(forms.Form):
title = forms.CharField(max_length=20)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.add_input(Submit('search', 'Search', css_class='btn'))
self.helper.form_method = 'GET'
self.helper.layout = Layout('title')
super(SearchBookForm, self).__init__(*args, **kwargs)
------------------UPDATE------------------
Though I understand Daniel Roseman's answer but as I am fairly new to django, I am not sure how to implement the whole thing, hitting plenty of "X not accessible, X is not attribute of Y" and etc. After much digging, I found some other useful posts on this same matter.
Django: Search form in Class Based ListView
Updating context data in FormView form_valid method?
Django CBV: Easy access to url parameters in get_context_data()?
Django class based view ListView with form
URL-parameters and logic in Django class-based views (TemplateView)
Another problem I encounter is I am unable to access the parameters in URL using self.kwargs as what suggested in most of the posts. In the final link I posted above, Ngenator mentioned that URL parameters has to be accessed using request.GET.get('parameter'). I used that and it's working fine for me.
By combining everything, here's the revised piece of coding I have. Just in case anyone is having the same problem as me.
1) views.py
class SearchBookView(ListView):
template_name = 'books/search_book.html'
paginate_by = '3'
context_object_name = 'book_found'
form_class = SearchBookForm
model = Book
def get_queryset(self):
object_list = self.model.objects.all()
title = self.request.GET.get('title', None)
if title is not None and title != "":
object_list = object_list.filter(title__icontains=title)
else:
object_list = []
return object_list
def get_context_data(self):
context = super(SearchBookView, self).get_context_data()
form = self.form_class(self.request.GET or None)
context.update({
'form': form,
})
return context
2) search_book.html (template)
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load staticfiles %}
{% load bootstrap_pagination %}
{% block title %}Search Page{% endblock %}
{% block content %}
<div class="container">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
{% crispy form %}
{% if book_found %}
<p>Found {{ paginator.count }} book{{ book_found_no|pluralize }}.</p>
{% for book in book_found %}
<div class="wholecard">
<div style="height:170px; border:solid #111111;" class="col-md-3">
Image
</div>
<div class="card col-md-9">
<div class="card-title">"{{ book.title }}"</div>
<div>{{ book.description }}</div>
Read More
</div>
</div>
{% endfor %}
{% else %}
<p>No book matched your searching criteria.</p>
{% endif %}
{% bootstrap_paginate page_obj %}
</div>
{% endblock %}
And I ended up using jmcclell's bootstrap-pagination also for pagination. Saved me lots of time! Good stuff...
You've specifically overridden the get method so that it defines its own context, and never calls the default methods, so naturally none of the default context bars are available.
Don't do that; you should almost never be overriding the get and post methods. You should probably move all the form stuff directly into get_queryset.
It's working
views.py
class UserListView(ListView):
model = User
template_name = 'user_list.html'
context_object_name = 'users'
paginate_by = 10
def get_queryset(self):
return User.objects.all()
templates/user_list.html
{% if is_paginated %}
<nav aria-label="Page navigation conatiner">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li>« PREV </li>
{% else %}
<li class="disabled page-item"><a class="page-link">PREV !</a></li>
{% endif %}
{% for i in %}
{{ i }}
{% endfor %}
{% if page_obj.has_next %}
<li> NEXT »</li>
{% else %}
<li class="disabled page-item"><a class="page-link">NEXT !</a></li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}