1

I am trying to build a small online plant database, and would like to incorporate a search engine. I am building my application with Flask and I have the application connected to a MySQL database using SQLAlchemy and pymysql. I have been fiddling with Flask-WhooshAlchemy and so far am having no luck. What I would like have is a search where if users search for a specific plant that it will either go to the plant info page or return results of similar species. For example, if they were to search for 'Carex utriculata' it would bring them directly to the species info page for 'Carex utriculata'. But if they searched for 'Carex' they would get a results page listing all similar species so there might be a list something like 'Carex aquatilis' 'Carex keloggii''Carex utriculata'.

In my attempts so far it appears that a search is attempted, but the results display all the plants in my database, not just what was entered into the search. And I have not even tried to play with getting the search to go straight to an individual plant's page if it is an exact match with the search because I have been stuck on getting the search to work. I am working with Python 3.5. Here is my code.

init.py

import pymysql.cursors
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask_whooshalchemy as whooshalchemy


app = Flask(__name__)


app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:passwrd@localhost/restorationplantdb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['WHOOSE_BASE'] = 'results'


db = SQLAlchemy(app)

class SPInfo(db.Model):
    __tablename__ = 'speciesinfo'
    __searchable__ = ['NRCS_Species', 'NRCS_CommonName']
    speciesinfo_id = db.Column(db.Integer,primary_key=True)
    ID = db.Column(db.Integer)
    MTNHP_Species = db.Column(db.String(45))
    NRCS_Species = db.Column(db.String(45))
    NRCS_CommonName = db.Column(db.String(45))
    Synonyms = db.Column(db.Text)
    Sp_Stratum = db.Column(db.String(12))
    Origin = db.Column(db.String(25))
    Duration = db.Column(db.String(25))
    Habitat = db.Column(db.Text)
    Elevation = db.Column(db.Text)


whooshalchemy.whoosh_index(app, SPInfo)

import RestorationPlantDB.views

views.py

from flask import render_template, url_for, redirect, session, request
from datetime import datetime
from RestorationPlantDB import app
from RestorationPlantDB import db
from RestorationPlantDB import SPInfo



@app.route('/search', methods = ['GET','POST'])
def search():
    results = SPInfo.query.all()
    return render_template('search.html', results=results)

@app.route('/results')
def results():
    results = SPInfo.query.whoosh_search(request.args.get('query')).all()
    return render_template('search.html', results=results)

search.html

{% extends "layout.html" %}

{% block content %}


{% for result in results %}
<p>{{ result.NRCS_Species }} - {{ result.NRCS_CommonName }}</p>
{% endfor %}



<div class="col-sm-3 col-sm-offset-1 blog sidebar">
    <div class="sidebar-module sidebar-module-insert">
        <h4>Search</h4>
        <form class="form-inline" method="GET" action="search">
            <div class="form-group">
                <label for="query">Name</label>
                <input type="text" class="form-control" name="query" id="query" />
            </div>
            <button type="submit" class="btn btn-primary">Search</button>
        </form>
    </div>
</div>


{% endblock %}

1 Answers1

0

All you're doing is querying everything, which is why you get everything. You're filtering the query under /results but the form doesn't go there. Rather than separating them out like that, you could easily just do this:

@app.route('/search', methods = ['GET','POST'])
def search(results=None):
    if request.method == 'POST':
        results = SPInfo.query.whoosh_search(request.form.get('query')).all()
    return render_template('search.html', results=results)

and remove /results entirely. Next, you would want to implement redirecting to an exact match. I would add this into the above code for that:

from sqlalchemy import func

@app.route('/search', methods = ['GET','POST'])
def search(results=None):
    if request.method == 'POST':
        keywords = request.form.get('query')
        unique_result = SPInfo.query.filter(func.lower(SPInfo.NRCS_Species) == func.lower(keywords)).first() or SPInfo.query.filter(func.lower(SPInfo.NRCS_CommonName) == func.lower(keywords)).first()
        if unique_result:
            #insert return redirect() to specific page using unique_result here
        results = SPInfo.query.whoosh_search(keywords).all()
    return render_template('search.html', results=results)

Normally I would've just done:

unique_result = SPInfo.query.filter_by(NRCS_Species=keywords).first() or SPInfo.query.filter_by(NRCS_CommonName=keywords).first()

but I assume that you want it to be case insensitive. If you don't, replace the queries with this one.

I also left the redirect blank because I don't know what your route looks like for the species page, but it shouldn't be hard to figure out.

EDIT

Changed the function definitions, and a minor change needs to be added to the template, like so:

{% if results %}
    {% for result in results %}
        <p>{{ result.NRCS_Species }} - {{ result.NRCS_CommonName }}</p>
    {% endfor %}
{% endif %}
Taehan Stott
  • 479
  • 3
  • 10
  • Thank for your reply. I think that is getting closer to resolving the issue. But now its throwing an error at me. UnboundLocalError: local variable 'results' referenced before assignment. I have looked into the error and found that others have run into this a lot, and hate to ask the same question other have asked several times..but I tried some of the fixes recommended like trying to make results global. The fixes I tried do not seem to work. Any suggestions to fix this error? – DazedAndConfusedNewby Dec 06 '16 at 20:43
  • @DazedAndConfusedNewby Oh I see. All you need to do is replace `def search():` with `def search(results=None):` – Taehan Stott Dec 06 '16 at 20:45
  • @DazedAndConfusedNewby Actually, you might also have to edit the template to accept an empty results variable. I'll edit the answer accordingly. – Taehan Stott Dec 06 '16 at 20:47
  • Thanks. Your edit fixed the UnboundLocalError. Now it is back to searching but not posting any results. Here is my output when I run a search. 127.0.0.1 - - [06/Dec/2016 14:24:39] "GET /search?query=Carex+utriculata HTTP/1.1" 200 - [Search page with no results showing][1] [1]: i.stack.imgur.com/U5siX.jpg I am not sure what is up. Any more thoughts? If I really need to I will switch to postgresql instead of MySql but I am hoping that is not necessary. – DazedAndConfusedNewby Dec 06 '16 at 22:46
  • @DazedAndConfusedNewby I've been a bit busy, but today I had some time to look into it. It seems that flask-whooshalchemy is outdated and the devs for it aren't particularly active. I attempted to use some forked version but they failed as well. I've been needing to implement some search functionality for a project of mine, so I'll get back to you once I figure out an alternative solution. – Taehan Stott Dec 09 '16 at 22:45
  • @taehen stott. Thanks for looking at it again and taking your time. I actually got it to work finally, and am just working out a few kinks and I will post the answer I found. I ended up switching my virtual environment to python 2.7, and then I went back to a couple tutorials (https://www.youtube.com/watch?v=KE_g1MuaU-Y). It was not working primarily because I was not starting with a new table so the flask-whooshalchemy was not indexing the table. It does not work if you want to work with existing tables. So I created a new table with only species name info that I want to be able to search. – DazedAndConfusedNewby Dec 12 '16 at 21:30