full text search

This commit is contained in:
Miguel Grinberg 2012-12-16 00:35:16 -08:00
parent 1886654ff9
commit 8a27076ae4
6 changed files with 42 additions and 4 deletions

View File

@ -28,3 +28,5 @@ class EditForm(Form):
class PostForm(Form): class PostForm(Form):
post = TextField('post', validators = [Required()]) post = TextField('post', validators = [Required()])
class SearchForm(Form):
search = TextField('search', validators = [Required()])

View File

@ -1,5 +1,7 @@
from hashlib import md5 from hashlib import md5
from app import db from app import db
from app import app
import flask.ext.whooshalchemy as whooshalchemy
ROLE_USER = 0 ROLE_USER = 0
ROLE_ADMIN = 1 ROLE_ADMIN = 1
@ -71,10 +73,14 @@ class User(db.Model):
return '<User %r>' % (self.nickname) return '<User %r>' % (self.nickname)
class Post(db.Model): class Post(db.Model):
__searchable__ = ['body']
id = db.Column(db.Integer, primary_key = True) id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(140)) body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime) timestamp = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self): def __repr__(self):
return '<Post %r>' % (self.text) return '<Post %r>' % (self.body)
whooshalchemy.whoosh_index(app, Post)

View File

@ -11,6 +11,7 @@
<a href="{{ url_for('index') }}">Home</a> <a href="{{ url_for('index') }}">Home</a>
{% if g.user.is_authenticated() %} {% if g.user.is_authenticated() %}
| <a href="{{ url_for('user', nickname = g.user.nickname) }}">Your Profile</a> | <a href="{{ url_for('user', nickname = g.user.nickname) }}">Your Profile</a>
| <form style="display: inline;" action="{{url_for('search')}}" method="post" name="search">{{g.search_form.hidden_tag()}}{{g.search_form.search(size=20)}}<input type="submit" value="Search"></form>
| <a href="{{ url_for('logout') }}">Logout</a> | <a href="{{ url_for('logout') }}">Logout</a>
{% endif %} {% endif %}
</div> </div>

View File

@ -0,0 +1,9 @@
<!-- extend base layout -->
{% extends "base.html" %}
{% block content %}
<h1>Search results for "{{query}}":</h1>
{% for post in results %}
{% include 'post.html' %}
{% endfor %}
{% endblock %}

View File

@ -1,10 +1,10 @@
from flask import render_template, flash, redirect, session, url_for, request, g from flask import render_template, flash, redirect, session, url_for, request, g
from flask.ext.login import login_user, logout_user, current_user, login_required from flask.ext.login import login_user, logout_user, current_user, login_required
from app import app, db, lm, oid from app import app, db, lm, oid
from forms import LoginForm, EditForm, PostForm from forms import LoginForm, EditForm, PostForm, SearchForm
from models import User, ROLE_USER, ROLE_ADMIN, Post from models import User, ROLE_USER, ROLE_ADMIN, Post
from datetime import datetime from datetime import datetime
from config import POSTS_PER_PAGE from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS
@lm.user_loader @lm.user_loader
def load_user(id): def load_user(id):
@ -17,6 +17,7 @@ def before_request():
g.user.last_seen = datetime.utcnow() g.user.last_seen = datetime.utcnow()
db.session.add(g.user) db.session.add(g.user)
db.session.commit() db.session.commit()
g.search_form = SearchForm()
@app.errorhandler(404) @app.errorhandler(404)
def internal_error(error): def internal_error(error):
@ -118,6 +119,7 @@ def edit():
form = form) form = form)
@app.route('/follow/<nickname>') @app.route('/follow/<nickname>')
@login_required
def follow(nickname): def follow(nickname):
user = User.query.filter_by(nickname = nickname).first() user = User.query.filter_by(nickname = nickname).first()
if user == None: if user == None:
@ -136,6 +138,7 @@ def follow(nickname):
return redirect(url_for('user', nickname = nickname)) return redirect(url_for('user', nickname = nickname))
@app.route('/unfollow/<nickname>') @app.route('/unfollow/<nickname>')
@login_required
def unfollow(nickname): def unfollow(nickname):
user = User.query.filter_by(nickname = nickname).first() user = User.query.filter_by(nickname = nickname).first()
if user == None: if user == None:
@ -152,3 +155,19 @@ def unfollow(nickname):
db.session.commit() db.session.commit()
flash('You have stopped following ' + nickname + '.') flash('You have stopped following ' + nickname + '.')
return redirect(url_for('user', nickname = nickname)) return redirect(url_for('user', nickname = nickname))
@app.route('/search', methods = ['POST'])
@login_required
def search():
if not g.search_form.validate_on_submit():
return redirect(url_for('index'))
return redirect(url_for('search_results', query = g.search_form.search.data))
@app.route('/search_results/<query>')
@login_required
def search_results(query):
results = Post.query.whoosh_search(query, MAX_SEARCH_RESULTS).all()
return render_template('search_results.html',
query = query,
results = results)

View File

@ -13,6 +13,7 @@ OPENID_PROVIDERS = [
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository') SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
WHOOSH_BASE = os.path.join(basedir, 'search.db')
# mail server settings # mail server settings
MAIL_SERVER = 'localhost' MAIL_SERVER = 'localhost'
@ -25,4 +26,4 @@ ADMINS = ['you@example.com']
# pagination # pagination
POSTS_PER_PAGE = 3 POSTS_PER_PAGE = 3
MAX_SEARCH_RESULTS = 50