Permalink
Browse files

Checking in an example project, hosted at

  • Loading branch information...
1 parent 3120b28 commit 5f02ebc39178ec6def219c0a7fd0dce2f7aab805 @coleifer committed Nov 24, 2010
View
@@ -1,3 +1,4 @@
include LICENSE
include README.rst
include TODO.rst
+recursive-include example *
View
@@ -0,0 +1,246 @@
+import datetime
+import peewee
+
+from flask import Flask, request, session, g, redirect, url_for, \
+ abort, render_template, flash
+from functools import wraps
+from hashlib import md5
+
+# config
+DATABASE = 'tweepee.db'
+DEBUG = True
+SECRET_KEY = 'hin6bab8ge25*r=x&+5$0kn=-#log$pt^#@vrqjld!^2ci@g*b'
+
+app = Flask(__name__)
+app.config.from_object(__name__)
+
+database = peewee.Database(DATABASE)
+
+# model definitions
+class User(peewee.Model):
+ username = peewee.CharField()
+ password = peewee.CharField()
+ email = peewee.CharField()
+ join_date = peewee.DateTimeField()
+
+ class Meta:
+ database = database
+
+ def following(self):
+ return User.select().join(
+ Relationship, on='to_user_id'
+ ).where(from_user=self).order_by('username')
+
+ def followers(self):
+ return User.select().join(
+ Relationship
+ ).where(to_user=self).order_by('username')
+
+ def is_following(self, user):
+ return Relationship.select().where(
+ from_user=self,
+ to_user=user
+ ).count() > 0
+
+ def gravatar_url(self, size=80):
+ return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \
+ (md5(self.email.strip().lower().encode('utf-8')).hexdigest(), size)
+
+
+class Relationship(peewee.Model):
+ database = database
+ from_user = peewee.ForeignKeyField(User, related_name='relationships')
+ to_user = peewee.ForeignKeyField(User, related_name='related_to')
+
+ class Meta:
+ database = database
+
+
+class Message(peewee.Model):
+ database = database
+ user = peewee.ForeignKeyField(User)
+ content = peewee.TextField()
+ pub_date = peewee.DateTimeField()
+
+ class Meta:
+ database = database
+
+
+# utils
+def create_tables():
+ database.connect()
+ User.create_table()
+ Relationship.create_table()
+ Message.create_table()
+
+def auth_user(user):
+ session['logged_in'] = True
+ session['user'] = user
+ session['username'] = user.username
+ flash('You are logged in as %s' % (user.username))
+
+def login_required(f):
+ @wraps(f)
+ def inner(*args, **kwargs):
+ if not session.get('logged_in'):
+ return redirect(url_for('login'))
+ return f(*args, **kwargs)
+ return inner
+
+def object_list(template_name, qr, var_name='object_list', **kwargs):
+ kwargs.update(
+ page=int(request.args.get('page', 1)),
+ pages=qr.count() / 20 + 1
+ )
+ kwargs[var_name] = qr.paginate(kwargs['page'])
+ return render_template(template_name, **kwargs)
+
+# custom filters
+@app.template_filter('is_following')
+def is_following(from_user, to_user):
+ return from_user.is_following(to_user)
+
+# request handlers
+@app.before_request
+def before_request():
+ g.db = database
+ g.db.connect()
+
+@app.after_request
+def after_request(response):
+ g.db.close()
+ return response
+
+# views
+@app.route('/')
+def homepage():
+ if session.get('logged_in'):
+ return private_timeline()
+ else:
+ return public_timeline()
+
+@app.route('/private/')
+def private_timeline():
+ user = session['user']
+ following_ids = [u.id for u in user.following()]
+ messages = Message.select().where(user_id__in=following_ids).order_by(('pub_date', 'desc'))
+ return object_list('private_messages.html', messages, 'message_list')
+
+@app.route('/public/')
+def public_timeline():
+ messages = Message.select().order_by(('pub_date', 'desc'))
+ return object_list('public_messages.html', messages, 'message_list')
+
+@app.route('/join/', methods=['GET', 'POST'])
+def join():
+ if request.method == 'POST' and request.form['username']:
+ try:
+ user = User.get(username=request.form['username'])
+ flash('That username is already taken')
+ except StopIteration:
+ user = User.create(
+ username=request.form['username'],
+ password=md5(request.form['password']).hexdigest(),
+ email=request.form['email'],
+ join_date=datetime.datetime.now()
+ )
+ auth_user(user)
+ return redirect(url_for('homepage'))
+
+ return render_template('join.html')
+
+@app.route('/login/', methods=['GET', 'POST'])
+def login():
+ if request.method == 'POST' and request.form['username']:
+ try:
+ user = User.get(
+ username=request.form['username'],
+ password=md5(request.form['password']).hexdigest()
+ )
+ except StopIteration:
+ flash('The password entered is incorrect')
+ else:
+ auth_user(user)
+ return redirect(url_for('homepage'))
+
+ return render_template('login.html')
+
+@app.route('/logout/')
+def logout():
+ session.pop('logged_in', None)
+ flash('You were logged out')
+ return redirect(url_for('homepage'))
+
+@app.route('/following/')
+@login_required
+def following():
+ user = session['user']
+ return object_list('user_following.html', user.following(), 'user_list')
+
+@app.route('/followers/')
+@login_required
+def followers():
+ user = session['user']
+ return object_list('user_followers.html', user.followers(), 'user_list')
+
+@app.route('/users/')
+def user_list():
+ users = User.select().order_by('username')
+ return object_list('user_list.html', users, 'user_list')
+
+@app.route('/users/<username>/')
+def user_detail(username):
+ try:
+ user = User.get(username=username)
+ except StopIteration:
+ abort(404)
+ messages = user.message_set.order_by(('pub_date', 'desc'))
+ return object_list('user_detail.html', messages, 'message_list', user=user)
+
+@app.route('/users/<username>/follow/', methods=['POST'])
+@login_required
+def user_follow(username):
+ try:
+ user = User.get(username=username)
+ except StopIteration:
+ abort(404)
+ r = Relationship.get_or_create(
+ from_user=session['user'],
+ to_user=user,
+ )
+ flash('You are now following %s' % user.username)
+ return redirect(url_for('user_detail', username=user.username))
+
+@app.route('/users/<username>/unfollow/', methods=['POST'])
+@login_required
+def user_unfollow(username):
+ try:
+ user = User.get(username=username)
+ except StopIteration:
+ abort(404)
+ Relationship.delete().where(
+ from_user=session['user'],
+ to_user=user,
+ ).execute()
+ flash('You are no longer following %s' % user.username)
+ return redirect(url_for('user_detail', username=user.username))
+
+@app.route('/create/', methods=['GET', 'POST'])
+@login_required
+def create():
+ user = session['user']
+ if request.method == 'POST' and request.form['content']:
+ message = Message.create(
+ user=user,
+ content=request.form['content'],
+ pub_date=datetime.datetime.now()
+ )
+ flash('Your message has been created')
+ return redirect(url_for('user_detail', username=user.username))
+
+ return render_template('create.html')
+
+
+# allow running from the command line
+if __name__ == '__main__':
+ app.run()
View
@@ -0,0 +1,5 @@
+flask
+werkzeug
+jinja2
+gunicorn
+-e git+git@github.com:coleifer/peewee.git#egg=peewee
View
@@ -0,0 +1,16 @@
+body { font-family: sans-serif; background: #eee; }
+a, h1, h2 { color: #377BA8; }
+h1, h2 { font-family: 'Georgia', serif; margin: 0; }
+h1 { border-bottom: 2px solid #eee; }
+h2 { font-size: 1.2em; }
+
+.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
+ padding: 0.8em; background: white; }
+.page ul { list-style-type: none; }
+.page li { clear: both; }
+.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
+ margin-bottom: 1em; background: #fafafa; }
+.flash { background: #CEE5F5; padding: 0.5em;
+ border: 1px solid #AACBE2; }
+.avatar { display: block; float: left; margin: 0 10px 0 0; }
+.message-content { min-height: 80px; }
@@ -0,0 +1,11 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Create</h2>
+ <form action="{{ url_for('create') }}" method=post>
+ <dl>
+ <dt>Message:</dt>
+ <dd><textarea name="content"></textarea></dd>
+ <dd><input type="submit" value="Create" /></dd>
+ </dl>
+ </form>
+{% endblock %}
@@ -0,0 +1,5 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Home</h2>
+ <p>Welcome to the site!</p>
+{% endblock %}
@@ -0,0 +1,2 @@
+<a class="avatar" href="{{ url_for('user_detail', username=message.user.username) }}"><img src="{{ message.user.gravatar_url() }}" /></a>
+<p class="message-content">{{ message.content|urlize }}</p>
@@ -0,0 +1,6 @@
+{% if page > 1 %}
+ <a class="prev" href="?page={{ page - 1 }}">Previous</a>
+{% endif %}
+{% if page < pages %}
+ <a class="next" href="?page={{ page + 1 }}">Next</a>
+{% endif %}
@@ -0,0 +1,17 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Join</h2>
+ <form action="{{ url_for('join') }}" method="post">
+ <dl>
+ <dt>Username:</dt>
+ <dd><input type="text" name="username"></dd>
+ <dt>Password:</dt>
+ <dd><input type="password" name="password"></dd>
+ <dt>Email:</dt>
+ <dd><input type="text" name="email">
+ <p><small>(used for gravatar)</small></p>
+ </dd>
+ <dd><input type="submit" value="Join">
+ </dl>
+ </form>
+{% endblock %}
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Tweepee</title>
+<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
+<div class=page>
+ <h1><a href="{{ url_for('homepage') }}">Tweepee</a></h1>
+ <div class=metanav>
+ {% if not session.logged_in %}
+ <a href="{{ url_for('login') }}">log in</a>
+ <a href="{{ url_for('join') }}">join</a>
+ {% else %}
+ <a href="{{ url_for('public_timeline') }}">public timeline</a>
+ <a href="{{ url_for('create') }}">create</a>
+ <a href="{{ url_for('logout') }}">log out</a>
+ {% endif %}
+ </div>
+ {% for message in get_flashed_messages() %}
+ <div class=flash>{{ message }}</div>
+ {% endfor %}
+ {% block body %}{% endblock %}
+</div>
@@ -0,0 +1,14 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Login</h2>
+ {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
+ <form action="{{ url_for('login') }}" method=post>
+ <dl>
+ <dt>Username:
+ <dd><input type=text name=username>
+ <dt>Password:
+ <dd><input type=password name=password>
+ <dd><input type=submit value=Login>
+ </dl>
+ </form>
+{% endblock %}
@@ -0,0 +1,10 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Private Timeline</h2>
+ <ul>
+ {% for message in message_list %}
+ <li>{% include "includes/message.html" %}</li>
+ {% endfor %}
+ </ul>
+ {% include "includes/pagination.html" %}
+{% endblock %}
@@ -0,0 +1,10 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Public Timeline</h2>
+ <ul>
+ {% for message in message_list %}
+ <li>{% include "includes/message.html" %}</li>
+ {% endfor %}
+ </ul>
+ {% include "includes/pagination.html" %}
+{% endblock %}
Oops, something went wrong.

0 comments on commit 5f02ebc

Please sign in to comment.