search all fields in django [duplicate] - django

I'm trying to build a search system, and I want to search by multiple fieldsname, state, city, in my django models. I wrote the below code, yet I've been unable to figure out how to go about it.
Models:
class Finhall(models.Model):
user=models.ForeignKey(User)
name=models.CharField(max_length=250, unique=True)
address=models.CharField(max_length=200)
city=models.CharField(max_length=200)
state=models.CharField(max_length=200)
def __unicode__(self):
return u'%s' % (self.name)
Views.py
def hup_find(request):
if ('q' in request.GET) and request.GET['q'].strip():
query_string=request.GET.get('q')
seens=Finhall.objects.filter(name__icontains=query_string)
else:
seens=None
return render_to_response('find.html',{'seens':seens},context_instance=RequestContext(request))
Template:
{% block content %}
<body>
<form action="" method="GET">
<input type="text" name="q" />
<button type="submit">search</button>
</form>
{% for seen in seens %}
<p> {{seen.name}}</p>
{% empty %}
<p> no search </p>
{% endfor %}
</body>
{% endblock %}
How can I go about this? I don't want to use haysatck due to some personal reasons.

you can use django Q objects to do OR query,
or if you want to ANDyour queries together just use the current lookups as kwargs
seens = Finhall.objects.filter(
name__icontains=query_string,
address__icontains=query_string
)
You should really consider full text search or haystack (which makes search easy) because icontains issues a %LIKE% which is not remotely scalable

EDIT: Just noticed it is Postgres only
Apparently in django 1.10 SearchVector class was added.
Usage from the docs:
Searching against a single field is great but rather limiting. The Entry instances we’re searching belong to a Blog, which has a tagline field. To query against both fields, use a SearchVector:
>>> from django.contrib.postgres.search import SearchVector
>>> Entry.objects.annotate(
... search=SearchVector('body_text', 'blog__tagline'),
... ).filter(search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]

To search same text in multiple fields you can use this :
from django.db.models import Q
class SearchAPI(APIView):
def get(self, request, search_text, format=None, **kwargs):
Model.objects.filter(Q(search_tags__contains=search_text) | Q(auto_tags__contains=search_text)

As Robin Nemeth suggested, if you are using postgres db, can use the SearchVector, (I'm just making it straight to the question, get full details in django documentation.)
First add django.contrib.postgres to the INSTALLED_APPS, helps leveraging PostgreSQL’s full text search engine.
in the views.py of the search,
from django.contrib.postgres.search import SearchVector
# view function
def hup_find(request):
search_vector = SearchVector('name', 'state', 'city')
if ('q' in request.GET) and request.GET['q'].strip():
query_string=request.GET['q']
seens = Findhall.objects.annotate(
search=search_vector).filter(search=query_string)
return render(request, 'find.html', {'seens':seens})
For large databases the search process takes some time, you can improve the performance using use GIN-(Generalized Inverted Index) feature of postgresql, These are some helpful links other than the official documentation,
Mastering django Search.
This stackoverflow question.
There is different approach found, I think employing search in a more pythonish way by Julien Phalip,

Related

Django & AJAX to show DB objects upon user's input submission

I'm pretty new in the Web development world, have been using Django so far.
I've been trying to figure out how to render data back to page after clicking on a submit button, so I see I'll need to use AJAX for that purpose.
I've created a very simple app just to understand the basics of AJAX.
However, googling, I couldn't really find a basic straight-forward implementation so I kinda got lost...
What I'm trying to achieve:
I have a model called Country:
class Country(models.Model):
name = models.CharField(primary_key=True, max_length=35)
continent = models.CharField(max_length=10)
capital = models.CharField(max_length=35)
currency = models.CharField(max_length=10)
And a super simple main page that asks the user to insert some country name.
The idea is to bring to the page all the info from the DB.
So it would look like this:
Main page HTML body:
<body>
<h2><em>Please type a country name:</em></h2><br><br>
<div class="container">
<form id="get_info" method="post">
{{ form }}
{% csrf_token %}
<input id="submit_button" type="submit" name="submit" value="Get info">
</form>
</div>
</body>
views.py:
from django.shortcuts import render
from country_trivia import forms
def main_page(request):
get_info_form = forms.GetInfo()
return render(request, 'country_trivia/index.html', {'form': get_info_form})
forms.py:
from django import forms
class GetInfo(forms.Form):
country_name = forms.CharField(label="")
I've seen some examples using forms, but I'm not even sure if it's needed, as I've seen some other examples that count on 'onclick' even listeners, then "grab" the text in the search field and pass it via AJAX...
How should I build my AJAX object for that simple purpose, and how should I integrate it?
Do I need to use forms at all?
I don't post anything to DB, just query it and print out data...
Thanks!!

Django Alter commandline filter to use in project

So since i've been trying to learn django there is one thing that confuses me more than anything else, and that is getting something that works in the django shell into a format that works in models.py of views.py. So here is an example from the Documentation:
>>> Entry.objects.all().filter(pub_date__year=2006)
So I can work with the shell, there are loads of examples everywhere, what never seems to be covered is how you then put this in your code if you want to filter for other years for example. Can someone explain this or point me at documentation that explains this, as I've not found it in the django docs.
There are several ways to do that, but for the beginning and learning purposes your view should accept a year argument:
def my_view(request, year):
entries = Entry.objects.filter(pub_date__year=year)
context = {
"entries": entries
}
return TemplateResponse(request, 'my_template.html', context)
The line you are talking about is used to query the database and then filter things. This line is mostly used in views or serializers since you want the entries to be passed on to either template or as JSON to API response.
Using it in views:
def view(request):
entries = Entry.objects.filter(pub_date__year=year)
return render(request, 'index.html', {'entries': entries})
Here the object {'entries': entries} in above code is context object.
And then after that, you can use the same thing in Django Templates for example here in index.html
{% for entry in entries %}
<li> {{ entry.id }} </li>
{% endfor %}

How to fix 'Argument data to tableXXX is required' in django-tables2?

I'm setting up a webstore manager and I'm relying on django-tables2 package to show a list of products using the SimpleTableMixin. I want to add the filter/search capability to the view. As recommended by the django-tables2 package one can rely on django-filter package to provide filtering. However, it the case of a model with many fields it becomes almost impossible to query and develop forms for those efficiently. My goal is to use django-haystack to have a single input search form as a mean to query the model instances which should be displayed in a similar table/form.
I've tried to add the SimpleTableMixin to the django-haystack package generic SearchView. However I keep on getting the following error:
TypeError at /manager/front/products/
Argument data to ProductTable is required
So far my implementation goes as follow:
view:
# ############## Products ############## (previous implementation with django-filter)
# #method_decorator(staff_member_required, name='dispatch')
# class ProductList(SingleTableMixin, FilterView):
# model = Product
# table_class = tables.ProductTable
# filterset_class = filters.ProductFilter
# template_name = 'manager/front/products/product_list.html'
############## Products ##############
#method_decorator(staff_member_required, name='dispatch')
class ProductList(SingleTableMixin, SearchView):
model = Product
table_class = tables.ProductTable
template_name = 'manager/front/products/product_list.html'
tables:
import django_tables2 as tables
from front.models import Product
class ProductTable(tables.Table):
class Meta:
model = Product
template_name = 'django_tables2/bootstrap.html'
search_indexes.py:
from haystack import indexes
from front.models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Product
urls:
path('front/products',views.ProductList.as_view(),name="manage_products"),
template:
{% extends 'base_site.html' %}
{% load render_table from django_tables2 %}
{% block content_title %}Manage Products{% endblock%}
{% block content %}
<form action="" method="get" class="form form-inline">
{{ form }}
<button class="btn btn-default" type="submit">Search</button>
</form>
{% render_table table %}
{% endblock %}
How can I remove that error and provide efficient search abilities to my list views?
Are you sure that you are using haystack.generic_views.SearchView and not haystack.views.SearchView? Please notice that on https://django-haystack.readthedocs.io/en/latest/views_and_forms.html it says:
As of version 2.4 the views in haystack.views.SearchView are deprecated in favor of the new generic views in haystack.generic_views.SearchView which use the standard Django class-based views which are available in every version of Django which is supported by Haystack.
Thus if you're using haystack.views.SearchView then the get_context_data of SingleTableMixin will never be called thus no table will be put in your context (i.e table will be empty). Actually because I dislike the behavior of {% render_table %} when its parameter is empty (it behaves different than the other Django tags/filters i.e it throws an exception while the django ones silently ignore that) I usually put it inside some {% if table %} checks.
UPDATE
It seems that for whatever reason the data is not passed to the table. I am not sure why, I can't test it right now but from a quick look at the source code your implementation should have been working (considering that SearchView has a get_queryset and TableMixin uses the get_queryset to retrieve its data). In any case you try overriding some methods of TableMixin to make sure that the table is properly returned (take a look at TableMixin here: https://django-tables2.readthedocs.io/en/latest/_modules/django_tables2/views.html).
I think the most definite solution would be to just bite the bullet and override get_table yourself. So, try adding something like this to your class:
def get_table(self, **kwargs):
table_class = self.get_table_class()
# I only change this line from the original to make sure that the self.get_queryset() is called to return the data
table = table_class(data=self.get_queryset(), **kwargs)
return RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(
table
)
One idea that just popped in my mind. Is there a possibility that the get_queryset() method of SearchView returns None due to bad configuration or whatever?

Django Formview Won't Validate

Django newbie here. I recently implemented a simple search with Django. I user specifies data in search bar, search is executed. I was able to determine today how to get the search to be null if user clicks on empty search bar. What I'm really trying to accomplish is to implement error logic so that the user has to specify data in the search bar. This has been very straight forward with all of the other views, but the search is a bit trickier, for me anyway. I have done several SO searches today, and determined that my HTML get method is ok for a search, but it may be causing some problems with the FORMVIEW that I am using. I have also tried to override POST and GET for the FORMVIEW, but can't seem to get it to work. I want to stick with CLASS BASED VIEWS, so I am using FORMVIEW. Here is my code....
My HTML
<form method="GET" autocomplete=off action="{% url 'Book:book_request_search_results' %}" >
<div>
<h1 class="title">Book Request Search</h1>
</div>
{{ form.non_field.errors }}
<div class="section">
<input type="search" class="name2" name="q">
</div>
<div class="section">
<input type="submit" id="" class="submit6" value="Search">
</div>
My VIEWS.PY
class BookRequestSearchView(LoginRequiredMixin,FormView):
form_class = BookRequestSearch
template_name = 'store/Book_request_search.html'
success_url = reverse_lazy('store:book_request_search_results')
def form_valid(self, form):
self.kwargs['q'] = form.cleaned_data['q']
if self.request.GET.get('q'):
pass
else:
raise forms.ValidationError("Enter data")
return super(BookRequestSearchView, self).form_valid(form)
My FORMS.PY
class BookRequestSearch(forms.Form):
q = forms.CharField(required=True)
def __init__(self, *args, **kwargs):
super(BookRequestSearch, self).__init__(*args, **kwargs)
I also tried to add a clean method to my form but that doesn't seem to work either...
def clean(self):
search = self.initial['q']
if search:
pass
else:
raise forms.ValidationError("Enter data")
return self.cleaned_data
I've been scanning most of the afternoon and can't seem to find a way to trigger validation on the FORMVIEW to trigger an error on the search bar. Any help would be appreciated. I've seen many different FORMVIEW articles, but none of them have helped me understand what I'm doing wrong and why this isn't working. The way the code is now, it works, but it doesn't prevent user from clicking on search and then triggering essentially an empty query. I'm trying to prevent that by forcing the user to put something in the box if they try to search with no criteria. Thanks in advance for any direction.
I was wrong on saying that it's only possible with javascript.
It's so simple as adding the required attribute to your input form, so the browser catches the empty error.
<form>
<input type="search" required><!-- added the required attribute-->
</form>

Making a proper search function in Django

I'm trying to make a search function that allows me to enter the value of an object in an instance so I can display that instance and several of it's objects on the same page as the search page. Here's what I have so far:
#models.py
class Student(models.Model):
# STEP 1 BASIC INFO
student_id = models.CharField(max_length=128, unique=True)
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
ssn = USSocialSecurityNumberField(null=False)
gender = models.CharField(max_length=128, choices=GENDER_CHOICES)
dob = models.DateField(auto_now=False, auto_now_add=False, db_column="date of birth")
contact_number = models.CharField(max_length=128)
address = models.CharField(max_length=128)
city = models.CharField(max_length=128)
state = USStateField(choices=STATE_CHOICES, default='NJ')
zipcode = USZipCodeField(blank=True)
country = CountryField(default='US', blank=True)
home_phone = models.CharField(max_length=128)
cell_phone = models.CharField(max_length=128)
email = models.EmailField(max_length=254, validators=[validate_email])
def __str__(self):
return self.first_name + self.last_name
#views.py
def search_Student(request):
context_dict = {}
if request.method == 'POST':
query = request.POST['last_name_search']
results = Student.objects.filter(last_name=query)
if query:
context_dict['results'] = results
else:
context_dict['no_results'] = query
return render(request, "students/search_student.html", context_dict)
#search_student.html
{% block main_content %}
<form method="post" action="/students/search_student/">
{% csrf_token %}
<label for="last_name_search">Last Name:</label>
<input type="text" name="last_name_search" id="last_name_search">
<input type="submit" name="submit">
</form>
<div id="result_panel">
{% if no_results %}
No results returned for <q>{{ no_results }}</q>
{% else %}
{% for result in results %}
{{ result.last_name }}
{% endfor %}
{% endif %}
</div>
{% endblock %}
#urls.py
urlpatterns = [
url(r'^$', students_views.Students, name='students'),
url(r'^add_student/$', students_views.add_Student, name='add_student'),
url(r'^id=(?P<identify>[\w]+)/add_studentcourse/$', students_views.add_StudentCourse, name='add_studentcourse'),
url(r'^id=(?P<identify>[\w]+)/add_studentemployment/$', students_views.add_StudentEmployment, name='add_studentemployment'),
url(r'test/$', students_views.test, name='test'),
#URL for the search page.
url(r'^search_student/$', students_views.search_Student, name='search_student'),
url(r'^current_student/$', students_views.current_Student, name='current_student'),
url(r'^all_my_student/$', students_views.all_My_Student, name='all_my_student'),
url(r'^public_student/$', students_views.public_Student, name='public_student'),
url(r'^sales_reports/$', students_views.sales_Reports, name='sales_reports'),
url(r'^switch_counselor/$', students_views.switch_Counselor, name='switch_counselor'),
url(r'^source_admin/$', students_views.source_Admin, name='source_admin'),
url(r'^super_tool/$', students_views.super_Tool, name='super_tool'),
url(r'^help_and_settings/$', students_views.help_And_Settings, name='help_and_settings'),
]
Basically, if I type in a last name in the input, I want it to be able to grab all model instances that have a last name equal to that, and to be able to put any information I want about all the matching instances, like the name, gender, and address on the exact same page as the search. The POST method may be confusing, but someone insists that I do this (It didn't work when I changed the method to GET either). Could someone please point out any errors or missing pieces in my code? Thanks.
Edit: Added the urls.py.
How to debug this
Remove the action from the tag. The browser will automatically post to the same URL. This rules out any issues with redirects, mappings or upstream urlconfs.
Use View Source in the browser to make sure nothing is returned, as opposed to the div being hidden by CSS or JS.
Make sure results are found:
views.py
if query:
results = Student.objects.filter(last_name=query)
if results.count():
context_dict['results'] = results
else:
context_dict['no_results'] = query
template
<div id="result_panel">
{% if no_results %}
No results returned for <q>{{ no_results }}</q>
{% else %}
{% for result in results %}
{{ result.last_name }}
{% endfor %}
{% endif %}
</div>
Note that the above cannot ever be empty. It should always show something. If it does not, it is a display error and the data is there. See point 2.
If it shows the "no results" part, then try to replicate the query in the django shell:
python manage.py shell
>>> from yourapp.models import Student
>>> Student.objects.filter(last_name='what you typed')
>>> Student.objects.filter(last_name__iexact='what you typed')
>>> Student.objects.filter(last_name__icontains='what you typed')
>>> Student.objects.count()
If none of the first three returns results, then you made a typo or number 4 will show you have no students.
SO won't let me comment quite yet, so here to say that you can indeed (and are) supposed to treat dicts like a list in django templating seen here.
Can you please post your error? I'm not sure I'm seeing whats wrong. Your method adapts over to my code just fine, so I'm a bit lost.
Editing for formatting:
You should maybe make a compile time dictionary, IE:
context_dict = {
"results": results
}
return render(request, "students/search_student.html", context_dict)
Make sure to return in the if, so the scope is in the proper place, and if your if finds nothing return a variable saying so.
If you're bent on sticking to your answer, trying referencing it in the template as so..
{% for result in results.results %}
Your dictionary has a results entry, that points to the variable results. I believe you are iterating through the dictionary in your current example, as opposed to all the queries.
Of course you can use a view method and try to find the error in your custom search and template code.
OR you can do it in a more Django way, relying on pre-built features of Django:
Use Django's ListView to query on and display the Student Model objects. You get paging, error handling, context setup for free with this. See other StackOverflow questions like https://stackoverflow.com/a/33350839/621690 for example code.
Your code that filters the Students would go into get_queryset - or you can use django-filter.
For nice usability, you can add Select2 to your form input which allows autocomplete/lookahead. See django-select2