I am trying to implement a search feature with pagination.
I have successfully either managed to get the search to work or the pagination to work. But I cannot figure out how to get both of them working together at the same time.
Here is the .html, by switching object_list to filter.qs in the .html, I can switch between either searching correctly or pagination correctly.
Can someone help me fix the code so that I can have both working together ?
{% extends 'base.html' %}
{% load widget_tweaks %}
{% load my_tags %}
{% block head %}
<title> Overview</title>
{% endblock %}
{% block content %}
<form method="get">
<div class="jumbotron">
<h4 style="margin-top: 0">Filter</h4>
<div class="row">
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.name.label_tag }}
{% render_field filter.form.name class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.city_location.label_tag }}
{% render_field filter.form.city_location class="form-control" %}
</div>
</div>
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span> Search
</button>
</div>
</form>
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>City Location</th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr>
<td>{{ object.name }}</td>
<td>{{ object.city_location }}</td>
</tr>
{% empty %}
<tr>
<td colspan="5">No data</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<br>
<div id="footer">
<div class="container text-center">
<p class="text-muted credit" style="color:#fff">
{% if is_paginated %}
{% if page_obj.has_previous %}
First
{% if page_obj.previous_page_number != 1 %}
Previous
{% endif %}
{% endif %}
Page {{ page_obj.number }} of {{ paginator.num_pages }}
{% if page_obj.has_next %}
{% if page_obj.next_page_number != paginator.num_pages %}
Next
{% endif %}
Last
{% endif %}
<p>Objects {{ page_obj.start_index }}—{{ page_obj.end_index }}</p>
{% endif %}
</div>
</div>
{% endblock %}
Here is my models
from django.db import models
# Create your models here.
class lab(models.Model):
name = models.CharField(max_length=255)
city_location = models.CharField(max_length=255)
def __str__(self):
return self.Lab_name
Here is my views.py
class labListView(LoginRequiredMixin, ListView):
context_object_name = "Lab_folders"
model = lab
template_name = "researcher_view_app/Lab_overview.html"
paginate_by = 20
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = labOverviewFilter(self.request.GET, queryset=self.get_queryset())
return context
Here is my filters.py
import django_filters
class labOverviewFilter(django_filters.FilterSet):
Lab_name = django_filters.CharFilter(lookup_expr='icontains')
Lab_city_location = django_filters.CharFilter(lookup_expr='icontains')
And the part which I have no idea about, that I do not know how to modify but works is: my tempatetag
app_name/templatetags/my_tags.py
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def param_replace(context, **kwargs):
"""
Return encoded URL parameters that are the same as the current
request's parameters, only with the specified GET parameters added or changed.
It also removes any empty parameters to keep things neat,
so you can remove a parm by setting it to ``""``.
For example, if you're on the page ``/things/?with_frosting=true&page=5``,
then
Page 3
would expand to
Page 3
Based on
https://stackoverflow.com/questions/22734695/next-and-before-links-for-a-django-paginated-query/22735278#22735278
"""
d = context['request'].GET.copy()
for k, v in kwargs.items():
d[k] = v
for k in [k for k, v in d.items() if not v]:
del d[k]
return d.urlencode()
You can apply filter in get_queryset method. Like this
class labListView(ListView):
context_object_name = "Lab_folders"
model = lab
template_name = "researcher_view_app/Lab_overview.html"
paginate_by = 20
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = labOverviewFilter(self.request.GET, queryset=self.get_queryset())
return context
def get_queryset(self):
qs = super().get_queryset()
word = labOverviewFilter(self.request.GET, queryset=qs)
return word.qs
pagination logic works after getting queryset. So provide filtered queryset to paginator.
Related
I'm trying to get data on_submit from input fields in multiple form fields. But I want to use one submit button from one of the fields. is this even possible?
class Form1(FlaskForm):
entry1 = StringField(('Entry 1'))
class Form2(FlaskForm):
entry2 = StringField(('Entry 2'))
submit = SubmitField(('Register'))
#app.route('/index', methods=['GET', 'POST'])
def index():
form1= Form1()
form2= Form2()
if form2.validate_on_submit():
entry1 = request.form.get('entry1')
entry2= request.form.get('entry2')
flash((entry1))
flash((entry2))
return redirect(url_for('main.index'))
return render_template('index.html', form1=form1, form2=form2)
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form1)}}
{{ wtf.quick_form(form2) }}
</div>
</div>
{% endblock %}
You could move the Submit button to a new class that inherits the other forms. From what I understand, validate_on_submit() processes and validates the fields of the called form, which includes any fields of inherited form classes.
class Form1(FlaskForm):
entry1 = StringField(('Entry 1'))
class Form2(FlaskForm):
entry2 = StringField(('Entry 2'))
class FinalForm(Form1, Form2):
submit = SubmitField(('Register'))
Now you only have to refer to the final form in the call and rendering.
#app.route('/', methods=['GET', 'POST'])
def index():
form = FinalForm()
if form.validate_on_submit():
entry1 = request.form.get('entry1')
entry2 = request.form.get('entry2')
flash((entry1))
flash((entry2))
return redirect(url_for('index'))
return render_template('index.html', form=form)
Here were the basic html templates I tested with success, trying to keep with the format you showed.
base.html :
{% extends 'bootstrap/base.html' %}
{% block content %}
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-info" role="alert">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block app_content %}{% endblock %}
</div>
{% endblock %}
index.html :
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}
I am not entirely sure if I understand your problem correctly, but you can use WTForms to build your forms. You'd have to define another class though that holds the both classes with the fields you need to submit.
https://wtforms.readthedocs.io/en/stable/
I'm making site for applications. And have two models: Applications and Guests and they have manytomany connection, because application can have 2 or more guests and 1 guest may be in many applications.
For making applications i use formset_factory with delete option. The problem is - I can't find any good examples with code for manytomanyfield and formset and I can't bound them in template. I can't display the table at all. Here's the code.
p.s. don't pay attention to cyrillic symbols
models
class Guests(models.Model):
unique_number_guest = models.IntegerField(unique=True, primary_key=True, verbose_name='№')
organisation = models.CharField(max_length=100, verbose_name='Организация', null=True)
full_name = models.CharField(max_length=100, verbose_name='ФИО')
position = models.CharField(max_length=100, verbose_name='Должность', blank=True, null=True)
chosen = models.BooleanField(default=False, verbose_name='Выбран')
class Applications(models.Model):
ntc_cabins = (
('2-5', '2-5'),
('2-10', '2-10'),
('2-12', '2-12'),
)
ntc_blocks = (
('ЦОК', 'ЦОК'),
('БМЗ', 'БМЗ')
)
unique_number_app = models.IntegerField(unique=False, null=True)
visit_date = models.DateField(default=date.today()+timedelta(days=1), verbose_name='Дата:')
visit_time = models.TimeField(default='12:00', verbose_name='Время:')
cabin = models.CharField(max_length=5, verbose_name='Кабинет:', choices=ntc_cabins, default='2-12')
block = models.CharField(max_length=10, verbose_name='Корпус:', choices=ntc_blocks, default='ЦОК')
author = models.CharField(max_length=100, verbose_name='Автор:')
guests = models.ManyToManyField(Guests)
views
def ApplicationFormation(request):
form = ApplicationsForm(request.POST)
form.guest_instanses = GuestsFormSet(request.POST)
if request.method == "POST":
if form.is_valid():
applications = Applications()
applications.cabin = form.cleaned_data['cabin']
applications.save()
for person in form.guests_visited:
guest = Guests()
guest.full_name = person['full_name']
guest.organisation = person['organisation']
guest.position = person['position']
guest.save()
applications.guests.add(guest)
return HttpResponse('good job')
print(form.errors)
else:
formset = GuestsFormSet()
print('FORMSET:', formset)
return render(request, 'myprofile/applications_form.html', {'form': form, 'fomset': formset})
forms
class GuestsForm(ModelForm):
organisation = forms.CharField()
full_name = forms.CharField()
position = forms.CharField()
class Meta:
model = Guests
exclude = ('unique_number_guest', 'chosen',)
GuestsFormSet = formset_factory(GuestsForm, extra=1, can_delete=True)
class ApplicationsForm(ModelForm):
visit_date = forms.DateField()
visit_time = forms.TimeField()
cabin = forms.CharField()
block = forms.CharField()
guests = GuestsFormSet()
class Meta:
model = Applications
exclude = ('unique_number_app', 'author', 'guests', 'ntc_cabin')
and template
{% extends "base.html" %}
{% load static %}
{% block title %}{% endblock %}
{% block content %}
<h2>Заявка на пропуск</h2>
<hr>
<div class="col-md-4">
<form action="" method="post" class="">
{% csrf_token %}
{{ form.as_p }}
<h3>Гости</h3>
<table id="table-guests">
{{ formset.management_form }}
{% for form in formset %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1 row2 %} formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Сохранить и сформировать заявку"/> Назад
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
$('.formset_row').formset({
addText: 'добавить',
deleteText: 'удалить',
prefix: 'Guests'
});
</script>
I made a small blog site and is not showing all the items from the database. If i try to see the posts from my phone i see only 3 posts, when in my DB there are 9. When i click on one post i got a Not Found error saying that the requested URL posts/legea etc was not found on server.
LE: My server is inside a VM. Is this maybe an issue?
This is the link that should have all of the posts: http://cohen.ro/posts/
PS I dont have a migrations folder. I just created now and put an init.py file in it. Bu still after running migrate didn't see the migrations and the result is the same.
However, if i enter in admin trhourgh site/admin i can see all of the posts in DB and when i go back to the site all of the items are out there and i can read the posts.
Can someone, please help me in order to fix this?! thank you!
My post List:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block head_title %} Blog | {% endblock %}
{% block content %}
<div class="container-fluid" style="width:91%;color: currentColor;!important;">
<div class="row" id="list_posts" style="background-color: white;padding-top: 40px;">
<br/>
<br/>
<br>
{% for obj in object_list %}
<div class="col-sm-4">
<div class="thumbnail">
{% if obj.image %}
<img src="{{ obj.image.url }}"/>
{% endif %}
<div class="caption" style="color:currentColor">
{% if obj.draft %} <h3>Staff Only Draft</h3> {% endif %}{% if obj.publish > today %}<h3>Staff Only: Future Post{%endif%}</h3>
<h2><a href='{{ obj.get_absolute_url }}' >{{obj.title }}</a> </h2>
<h5>{{obj.location }} </h5>
{% if obj.user.get_full_name %}<p>Author: {{ obj.user.get_full_name }}</p>{% endif %}
<p> {{ obj.content |linebreaks |truncatechars:150}}</p>
{# <p>View</p>#}
</div>
</div>
</div>
{# {% cycle "" "" "<div class='col-sm-12'><hr/></div></div><div class='row'style='background-color:white;color:currentColor'>" %}#}
{% cycle "" "" "<div class='col-sm-12'><hr/><br/></div><div class='row'style='background-color:white;color:currentColor'></div>" %}
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if object_list.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ object_list.number }} of {{ object_list.paginator.num_pages }}.
</span>
{% if object_list.has_next %}
next
last »
{% endif %}
</span>
</div>
</div>
</div>
Post Form:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block head_title %} Blog | {% endblock %}
{% block content %}
<div class="container-fluid" id="post_form_id" style="background-color: teal">
<div class="col-sm-6 offset-sm-3">
<h1> Form </h1>
<form method='POST' action='' enctype="multipart/form-data"> {% csrf_token %}
{{ form |crispy }}
<input type="submit" class="btn btn-default" role="button" value="Create Post">
</form>
</div>
</div>
{% include 'navbar_bottom.html' %}
{% endblock %}
My Post detail:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block head_title %} Blog | {% endblock %}
{% block content %}
<div class="container-fluid" id="detail_posts" style="background-color: white">
<div class="col-xs-12 offset-xs-0 col-sm-6 offset-sm-3">
{% if instance.image %}
<img src="{{ instance.image.url }}" class="img-responsive" />
{% endif %}
<h1> {{ title }} <small> {% if instance.draft %} <span style="color:red">{% trans "Draft" %}</span> {% endif %}</small></h1>
{% if instance.user.get_full_name %}
<p> {% trans "Autor:" %} {{ instance.user.get_full_name }}</p>
{% endif %}
{{ instance.location |linebreaks }} <br/>
{{ instance.content |linebreaks }} <br/>
Facebook
Twitter
<a href="https://www.linkedin.com/shareArticle?mini=true&url={{ request.build_absolute_uri }}/&title={{ instance.title }}&source={{ request.build_absolute_uri }}">
Linkedin </a>
Reddit
<a href='https://plus.google.com/share?url={{ request.build_absolute_uri }}'>GooglePlus</a>
</div>
</div>
{% include 'navbar_bottom.html' %}
{% endblock content%}
My views:
from contact.forms import ContactForm
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from contact.forms import ContactForm
from django.utils.translation import ugettext_lazy as _
import requests
from django.conf import settings
from django.contrib import messages
def index(request):
return render(request, 'home.html')
def board(request):
return render(request, 'board.html')
def persoanefizice(request):
return render(request, 'persoanefizice.html')
def consultanta(request):
return render(request, 'consultanta.html')
def cyberintelligence(request):
return render(request, 'cyberintelligence.html')
#
# def pay(request):
# return render(request, 'pay.html')
def blog(request):
return render(request, 'blog.html')
def contact(request):
if request.method == 'GET':
form = ContactForm()
else:
form = ContactForm(request.POST)
if form.is_valid():
contact_name = form.cleaned_data['numele_dumneavoastra']
contact_phone = form.cleaned_data['numarul_de_telefon']
# contact_period = form.cleaned_data['perioada_cand_va_putem_contacta']
subject = contact_name + " | " + contact_phone
content = form.cleaned_data['mesajul_dumneavoastra']
contact_email = form.cleaned_data['emailul_dumneavoastra']
''' Begin reCAPTCHA validation '''
recaptcha_response = request.POST.get('g-recaptcha-response')
data = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data)
result = r.json()
''' End reCAPTCHA validation '''
if result['success']:
try:
send_mail(subject,content,contact_email, ['greatjobdone770#gmail.com'])
except BadHeaderError:
return HttpResponse('Invalid header found.')
return redirect('success')
else:
form = ContactForm()
return render(request, "contact.html", {'form': form})
def success(request):
return HttpResponse('Succes! Multumim pt mesajul trimis.')
Paginator Number of Pages does not update in HTML after filtering with django_filter.
html file
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>
The page_obj.paginator.num_pages is the initial number (without any filters) of all results in the table (example: I got 12 results and showing 3 results/page => 4 pages)
views
class SearchBookView(ListView):
template_name = "booksearch.html"
paginate_by = 3
model = Book
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
book_qs = Book.objects.all()
book_filter = BookFilter(self.request.GET, queryset=book_qs)
paginator = Paginator(book_filter.qs, self.paginate_by)
print(paginator.num_pages) ### Prints the correct num pages everytime even after filtering
page = self.request.GET.get('page')
try:
book_list = paginator.page(page)
except PageNotAnInteger:
book_list = paginator.page(1)
except EmptyPage:
book_list = paginator.page(paginator.num_pages)
context['book_list'] = book_list
context['book_filter'] = book_filter
return context
After adding a filter (let's say after filtering it shows 5 results) page_obj.paginator.num_pages should be 2 in my HTML, right? Although in my view in print(paginator.num_pages) it shows 2, in the HTML it stays the original 4 pages. How can I pass this to the HTML file?
EDIT
filters
class BookFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='icontains')
author = django_filters.CharFilter(lookup_expr='icontains')
category = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Book
ields = ['name', 'author', 'category',]
full html
<h1 class="h1"> Search Books </h1>
<form method="get">
{{ book_filter.form.as_p }}
<button type="submit">Search</button>
</form>
<div class="container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Author</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{% for book in book_list %}
<tr>
<td>{{ book.name }}</td>
<td>{{ book.author }}</td>
<td>{{ book.category }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>
<span>Previous</span>
</li>
{% endif %}
<li class="">
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>
</li>
{% if page_obj.has_next %}
<li>
<span>Next</span>
</li>
{% endif %}
</ul>
{% else %}
<p>No books available</p>
{% endif %}
</div>
WORKAROUND
I did a workaround but it is kind of ugly:
in my view I added a context['num_pages'] = paginator.num_pages and pass it my HTML:
<span>Page {{ page_obj.number }} of {{ num_pages }}.</span>
Any suggestions on how to do this the correct way without adding a new context key, value pair?
You should set the queryset in the get_queryset method.
def get_queryset(self):
book_qs = Book.objects.all()
self.book_filter = BookFilter(self.request.GET, queryset=book_qs)
return book_filter.qs
Django will take care of paginating the queryset, and all you need to do in get_context_data is add book_filter to the context.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['book_filter'] = self.book_filter
return context
I'm using bootstrap to implement my post detail page.
It works well and fit perfectly into tho format when using Korean.
But, if I wrote english contents, it overflow the format.
What's wrong with it?
Here is the code :
models.py
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
def upload_location(instance, file_name):
return "{}/{}".format(instance.author.username, file_name)
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
image = models.ImageField(upload_to=upload_location, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('-created_at',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse(
"posts:detail",
kwargs={
"pk": self.id,
}
)
def get_image_url(self):
if self.image:
return self.image.url
return "http://placehold.it/300x200"
post_detail.html
{% extends 'chacha_dabang/skeleton/base.html' %}
{% load pipeline %}
{% block content %}
<div class="container">
<section class="post-content-section">
<h2> {{ post.title }} </h2>
<h5 style="text-align: right;"> {{ post.created_at }} </h5>
<hr class="thin">
<img src="{{ post.get_image_url }}" alt="image">
<p> {{ post.content }} </p>
</section>
</br>
<hr class="thin">
</br>
<section id="comment-section" data-post-id={{ post.pk }}>
<h3> 댓 글 (<span id="comment-count"></span>)</h3>
<ul>
</ul>
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
</section>
</div>
{% endblock %}
{% block custom_js %}
{% javascript "comments" %}
{% javascript "message" %}
{% endblock %}