Django select 2 multiple select slow load - django

I'm a beginner and I've been playing around with the multiple select option of select2.js. In a historical school database we have over 300k student_id's. I can get the select2 option to work, but it's extremely slow and takes forever to load. I've seen other pages with select 2 load massive amounts of data and work fine.
I'm using the following to javascript to load select2.
$(document).ready(function () {
$('.js-example-basic-multiple').select2();
});
In Django i'm loading the data in my template with:
<script src= "{% static '/search/user_select2.js' %}" type="text/javascript"></script>
<div class="col"><h4 style="margin-top: 0"><strong>Student ID List</strong></h4><select data-placeholder="Choose a list of 3-4 User ids..." class="js-example-basic-multiple" value = "{{ userid }}" style="width: 1110px" required>
{% for user in userid %}
<option value="{{ user }}"> {{ user }}</option>
{% endfor %}
</select>
userid is defined with the following arg in my view:
def multisearch(request):
userid = STudent.objects.filter(student_status_id = 1).values('studentid')
print(userid)
args = {'userid':userid}
return render(request, 'multisearch.html',args)

It takes a long time to load be cause you preload all options of your select2 input. I would suggest you to use select2 through django-autocomplete-light to avoid this issue. It will provide you tools to setup an autocomplete system and load matching options while typing text on your select2 input. Moreover, results paginated so that they are loaded as you scroll the select2 dropdown.
Basically you will have an autocomplete view:
from dal import autocomplete
from your_app.models import Student
class Status1StudentsAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Student.objects.filter(student_status_id=1)
if self.q: # self.q is the user-typed query
qs = qs.filter(name__istartswith=self.q)
return qs
That, of course, needs to be routed:
from your_app.views import Status1StudentsAutocomplete
urlpatterns = [
url(
r'^autocomplete/students/status-1/$',
Status1StudentsAutocomplete.as_view(),
name='students_status1_autocomplete',
),
]
Then use an autocomplete widget for your field:
from dal import autocomplete
from django import forms
class SomeForm(forms.Form):
student = forms.ModelChoiceField(
queryset=Student.objects.filter(student_status_id=1),
widget=autocomplete.ModelSelect2(url='student_status1_autocomplete')
)
Finally just display your form as you usually would and don't forget to include the custom css/js with {{ form.media }}.
{% extend "your_layout.html" %}
{% block extrahead %} {# assuming extrahead block is within the <head> element #}
{{ block.super }}
{{ form.media }}
{% endblock %}
{% block content %} {# assuming you have a content block within your <body> element #}
<form method="POST">
{% csrf_token %}
{{ form }}
<button type="submit">Go!</button>
</form>
{% endblock %}

Related

How do I change the search field label in Django admin?

In Django admin.py
class TableAdmin(admin.ModelAdmin):
search_fields = ["name", "description", "category"]
list_display = ["name", "description", "category"]
admin.site.register(Table, TableAdmin)
Here, we can change the list_display, label name by using below code,
name.short_description = "Product Name"
Now the question is, How do I change the search fields label name?
You can override django admin template's search_form.html file.
create a new html file similar to it, and name it search_form.html:
{% load i18n static %}
{% if cl.search_fields %}
<div id="toolbar"><form id="changelist-search" method="get">
<div><!-- DIV needed for valid HTML -->
<label for="searchbar"><img src="{% static "admin/img/search.svg" %}" alt="Search" /></label>
<input type="text" size="40" name="{{ search_var }}" value="{{ cl.query }}" id="searchbar" autofocus />
<input type="submit" value="{% trans 'My Search' %}" />
{% if show_result_count %}
<span class="small quiet">{% blocktrans count counter=cl.result_count %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktrans %} ({% if cl.show_full_result_count %}{% blocktrans with full_result_count=cl.full_result_count %}{{ full_result_count }} total{% endblocktrans %}{% else %}{% trans "Show all" %}{% endif %})</span>
{% endif %}
{% for pair in cl.params.items %}
{% if pair.0 != search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}"/>{% endif %}
{% endfor %}
</div>
</form></div>
{%endif %}
Then put it in your template directory's admin folder like:
(template directory)>admin>search_form.html
And it should override the label of search button with text 'My Search'
Is it possible to override it just for specific admin model(s)? Yes this is possible.
In order to do so you need to create following files.
YourApp
-> templatetags
-> __init__.py
-> custom_tags.py
-> admin_overrides
-> InclusionAdminNode
Now you have to register a custom tag) which renders the Search Form.
from django.contrib.admin.views.main import (
SEARCH_VAR,
)
from django.template import Library
from ..admin_overrides.InclusionAdminNode import InclusionAdminNode
register = Library()
def search_form(cl):
"""
Display a search form for searching the list.
"""
return {
'cl': cl,
'show_result_count': cl.result_count != cl.full_result_count,
'search_var': SEARCH_VAR
}
#register.tag(name='search_custom_form')
def search_form_tag(parser, token):
return InclusionAdminNode(parser, token, func=search_form, template_name='custom_search_form.html',
takes_context=False)
Now, we have to use this tag in our YourApp/change_list.html.
{% block search %}{% search_custom_form cl %}{% endblock %}
Note: Now still you will see that there is no change in the form. But here is the catch we have to add our custom InclusionAdminNode and change the template path. By default it pick from admin.
from inspect import getfullargspec
from django.template.library import InclusionNode, parse_bits
class InclusionAdminNode(InclusionNode):
"""
Template tag that allows its template to be overridden per model, per app,
or globally.
"""
def __init__(self, parser, token, func, template_name, takes_context=True):
self.template_name = template_name
params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(func)
bits = token.split_contents()
args, kwargs = parse_bits(
parser, bits[1:], params, varargs, varkw, defaults, kwonly,
kwonly_defaults, takes_context, bits[0],
)
super().__init__(func, takes_context, args, kwargs, filename=None)
def render(self, context):
opts = context['opts']
app_label = opts.app_label.lower()
object_name = opts.object_name.lower()
# Load template for this render call. (Setting self.filename isn't
# thread-safe.)
context.render_context[self] = context.template.engine.select_template([
'YourApp/%s/%s/%s' % (app_label, object_name, self.template_name),
'YourApp/%s/%s' % (app_label, self.template_name),
'YourApp/%s' % (self.template_name,),
])
return super().render(context)
That's it we are good to go now.
Hope this is helpful.
In the previous solution (of Nitish Kumar) you also need to add to change_list.html:
{% load custom_tags %}

Custom input field and custom searchqueryset

I followed the get started doc and everyting work well! :)
But i would like to replace the form in the /search/search.html by a custom form without selectable model checkbox.
In the form i would like to add a button which on click, order search results by a criteria
My questions are:
Which files i need to create or modified to perfoms that and what are their roles?
My codes are:
search_indexes.py
from haystack import indexes
from models import ProduitCommerce
class ProduitIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
commerce = indexes.CharField(model_attr = 'nom_commerce')
nom = indexes.CharField(model_attr = 'nom_produit')
price = indexes.DecimalField(model_attr = 'prix') #Field to filter ON
def get_model(self):
return ProduitCommerce
search/search.html
{% extends 'base.html' %}
{% block content %}
<h2>Search</h2>
<form method="get" action=".">
<table>
{{ form.as_table }} <!------ FORM TO CHANGE BY A CUSTOM FORM BLOCK TO INCLUDE WITH DJANGO ------->
<tr>
<td> </td>
<td>
<input type="submit" value="Search">
</td>
</tr>
</table>
{% if query %}
<h3>Results</h3>
<!---------------------------- PLACE TO INCLUDE THE BUTTON TO FILTER ON result.object.prix------------------>
{% for result in page.object_list %}
<p>
{{ result.object.nom_produit }}{{result.object.prix}}
</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 %}
</form>
produitcommerce_text.txt
{{ object.nom_produit }}
{{ object.nom_commerce }}
{{ object.description }}
{{ object.prix }}
PS: I'm working on a django project with the 1.5 .1 version and whoosh like haystack backend.
Thanks for you help :)
You are getting the selectable model checkboxes because you're extending the ModelSearchForm:
#forms.py
from haystack.forms import ModelSearchForm
class ProduitForm(ModelSearchForm):
You can easily avoid this behaviour by extending the simple search form variant, provided by the haystack:
#forms.py
from haystack.forms import SearchForm
class ProduitForm(SearchForm):
Then you're going to add something like the following to you application's urls.py:
#urls.py
from haystack.query import SearchQuerySet
from haystack.views import SearchView, search_view_factory
from myapp.models import Produit
urlpatterns = patterns('',
url(r'^produit/search/$', search_view_factory(
view_class=SearchView,
template='search/search.html',
searchqueryset=SearchQuerySet().models(Resume),
form_class=ProduitForm
), name='produit_search'),
)
Please note that there is a bug with .models() filter when trying to use it with latest versions of Haystack and Whoosh. If you would experience any kind of problems with the strategy above, then you should make sure, that your Haystack and Whoosh are of versions 2.0.0 and 2.4.1 relatively - tested and works well.
Also, not related to your question, you might want to avoid using HAYSTACK_SEARCH_RESULTS_PER_PAGE in your settings.py. It also has a very ugly bug. Just sharing my experience. Note that this info is related to Whoosh, everything should work fine with any other backend.

Completely stripping certain HTML Tags in Django forms

I have a ModelForm that posts news items to a database, and it uses a javascript textarea to allow the authorized poster to insert certain pieces of HTML to style text, like bold and italics. However, since I have the template output using the "safe" filter, it outputs all the HTML the form widget tries to pass on. This includes a bothersome <br> tag that never goes away, making it so you can submit without form validation reading the field as empty and stopping you. How can I make that I can not only filter the <br> tag, but completely remove it from the data? Here is relevant code:
Models.py:
from django.db import models
from django.forms import ModelForm, forms
from django.contrib.auth.models import User
# Create your models here.
class NewsItem(models.Model):
user = models.ForeignKey(User)
date = models.DateField(auto_now=True)
news = models.TextField(max_length=100000, blank=False, help_text='HELP TEXT')
def __unicode__(self):
return u'%s %s %s' % (self.user, self.date, self.news)
class NewsForm(ModelForm):
class Meta:
model = NewsItem
exclude=('user','date',)
Views.py:
from news.models import NewsForm, NewsItem
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse
def news(request):
if request.method == 'POST':
item = NewsItem(user=request.user)
form = NewsForm(request.POST, instance=item)
if form.is_valid():
form.save()
return HttpResponseRedirect('/news/')
else:
form = NewsForm()
news_list = NewsItem.objects.all()
return render(request, 'news_list.html', {'news_list': news_list, 'form': form})
news_list.html:
{% extends "base.html" %}
{% block title %}News in the Corps{% endblock %}
{% block content %}
<h2 id="page_h">News in the Corps</h2>
{% if user.is_authenticated %}
<h3>Post News</h3>
<script src="{{ STATIC_URL }}nicEdit.js" type="text/javascript"></script>
<script type="text/javascript">bkLib.onDomLoaded(nicEditors.allTextAreas);</script>
<div id="news_poster">
<form id="news_poster" action="/news/" method="POST">{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</div>
{% endif %}
<ul id="events_list">
{% if news_list %}
<div id="news_list">
{% for news in news_list %}
{% if news.id == 1 %}
<hr />
{% endif %}
<div id="{{ news.id }}" class="news_item">
<p class="poster">Posted By: {{ news.user }} | Posted On: {{ news.date }} | Link</p>
<div id="news_item">
{{ news.news|safe }}
</div>
</div>
<hr />
{% endfor %}
</div>
{% endif %}
</ul>
{% endblock %}
You can try the removetags template filter:
{{ news.news|removetags:"br"|safe }}
I can't help but thinking that the "removetags" as Timmy O'Mahony suggested might work if it was structured like this:
{{ news.news|safe|removetags:"br"}}
Give it a shot and see if it works. I would reply, but my karma's not height enough to directly reply to an answer with a suggestion.

Django template if statement always evaluates to true

This seems like it should be pretty straightforward, but for some reason I am unable to solve this problem. I'm using Django 1.4. I am trying to do a basic check to see if a list QuerySet is empty or not during template rendering, but the if statement I'm using seems always to evaluate to true.
I have a Django template that reads:
{% extends 'includes/base.html' %}
{% if object_list %}
...
{% block data %}
{% for object in object_list %}
...
{{ object.create_date }}
...
{% endfor %}
{% endblock data %}
...
{% endif %}
'base.html' has the block:
<body>
{% block content %}
...
<div class="row-fluid">
<div class="span12">
{% block data %}
<div align="center"><i>No data.</i></div>
{% endblock data %}
</div><!-- span12 -->
</div><!-- row -->
{% endblock content %}
...
</body>
The view function generating the QuerySet is here:
def barcode_track(request, model):
query = request.GET.get('barcode_search', '')
object_list = model.objects.all()
if query:
object_list = model.objects.filter(barcode__icontains=query)
return render_to_response('barcode_track/barcode_list.html',
{'object_list': object_list, 'query': query},
context_instance=RequestContext(request))
Which is called via this form:
<form id="barcode_search_form" method="get" action="" class="form">
<input type="text" name="barcode_search" value="{{ query }}" />
<button type="submit" class="btn">Search</button>
</form>
And the urls.py line:
urlpatterns = patterns('barcode_track.views',
url(r'^$', 'barcode_track', {'model': Barcode},
name="barcode_track"),)
The idea is that results will only be presented if they exist in object_list, and otherwise the parent block will remain unaltered. I have tried changing the name of object_list, and I have printed {{ dicts }} to the page to ensure that object_list is, in fact, empty (which it is). I am not using a generic view, although I realize that the name suggests as much. I have actually had this trouble in a different app I wrote using similar logic, so I must be doing something systematically incorrectly.
What am I missing here?
You can't wrap control flow tags like if around a block. Your problem is that the child template's definition for block data is being used simply because it's there.
You can fix it by placing the if tag inside block data. If you want to inherit the parent's contents when the list is empty, add an else case that expands to {{ block.super }}.

Editing a search view in Django

Hello I am working in django how to use a search form from Django documents site. Here are some information below.
Now, is there a way I can produce a part search for title? For example if I wanted to search for a book called "Apress", But instead of typing the whole word I just wrote "ap" to get Apress. Is there a way how to achieve this solution?
from django.db.models import Q
from django.shortcuts import render_to_response
from models import Book
#views.py
def search(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(title__icontains=query) |
Q(authors__first_name__icontains=query) |
Q(authors__last_name__icontains=query)
)
results = Book.objects.filter(qset).distinct()
else:
results = []
return render_to_response("books/search.html", {
"results": results,
"query": query
})
#search html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>Search{% if query %} Results{% endif %}</title>
</head>
<body>
<h1>Search</h1>
<form action="." method="GET">
<label for="q">Search: </label>
<input type="text" name="q" value="{{ query|escape }}">
<input type="submit" value="Search">
</form>
{% if query %}
<h2>Results for "{{ query|escape }}":</h2>
{% if results %}
<ul>
{% for book in results %}
<li>{{ book|escape }}</l1>
{% endfor %}
</ul>
{% else %}
<p>No books found</p>
{% endif %}
{% endif %}
</body>
</html>
If you want to match on the beginning of a field, you can use startswith or istartswith if you want it to be case sensitive. icontains which you are using now will allow matches even within strings, ie. 'arry' will match 'Harry'. While startswith will allow 'Har' to match 'Harry', but not 'ArHarHar'