ported LodgeIt to SQLAlchemy 0.6
This commit is contained in:
parent
bb26f0e6f1
commit
e53c92b3d9
@ -5,8 +5,7 @@
|
||||
|
||||
the WSGI application
|
||||
|
||||
:copyright: 2007 by Armin Ronacher, Christopher Grebs.
|
||||
2008 by Christopher Grebs.
|
||||
:copyright: 2007-2009 by Armin Ronacher, Christopher Grebs.
|
||||
:license: BSD
|
||||
"""
|
||||
import os
|
||||
@ -15,10 +14,12 @@ from babel import Locale
|
||||
from werkzeug import SharedDataMiddleware, ClosingIterator
|
||||
from werkzeug.exceptions import HTTPException, NotFound
|
||||
from sqlalchemy import create_engine
|
||||
from lodgeit import i18n, local
|
||||
from lodgeit import i18n
|
||||
from lodgeit.local import application, ctx, _local_manager
|
||||
from lodgeit.urls import urlmap
|
||||
from lodgeit.utils import COOKIE_NAME, Request, jinja_environment
|
||||
from lodgeit.database import metadata, session
|
||||
from lodgeit.database import db
|
||||
from lodgeit.models import Paste
|
||||
from lodgeit.controllers import get_controller
|
||||
|
||||
|
||||
@ -30,23 +31,24 @@ class LodgeIt(object):
|
||||
|
||||
#: bind metadata, create engine and create all tables
|
||||
self.engine = engine = create_engine(dburi, convert_unicode=True)
|
||||
metadata.bind = engine
|
||||
metadata.create_all(engine)
|
||||
db.metadata.bind = engine
|
||||
db.metadata.create_all(engine, [Paste.__table__])
|
||||
|
||||
#: jinja_environment update
|
||||
jinja_environment.globals.update(
|
||||
i18n_languages=i18n.list_languages()
|
||||
)
|
||||
jinja_environment.filters.update(
|
||||
datetimeformat=i18n.format_datetime
|
||||
)
|
||||
jinja_environment.globals.update({
|
||||
'i18n_languages': i18n.list_languages()})
|
||||
jinja_environment.filters.update({
|
||||
'datetimeformat': i18n.format_datetime})
|
||||
jinja_environment.install_null_translations()
|
||||
|
||||
#: bind the application to the current context local
|
||||
self.bind_to_context()
|
||||
|
||||
self.cleanup_callbacks = (db.session.close, _local_manager.cleanup,
|
||||
self.bind_to_context())
|
||||
|
||||
def bind_to_context(self):
|
||||
local.application = self
|
||||
ctx.application = application = self
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""Minimal WSGI application for request dispatching."""
|
||||
@ -72,7 +74,7 @@ class LodgeIt(object):
|
||||
expires=expires)
|
||||
|
||||
return ClosingIterator(resp(environ, start_response),
|
||||
[local._local_manager.cleanup, session.remove])
|
||||
self.cleanup_callbacks)
|
||||
|
||||
|
||||
def make_app(dburi, secret_key, debug=False, shell=False):
|
||||
@ -81,9 +83,9 @@ def make_app(dburi, secret_key, debug=False, shell=False):
|
||||
app = LodgeIt(dburi, secret_key)
|
||||
if debug:
|
||||
app.engine.echo = True
|
||||
app.bind_to_context()
|
||||
if not shell:
|
||||
# we don't need access to the shared data middleware in shell mode
|
||||
app = SharedDataMiddleware(app, {
|
||||
'/static': static_path
|
||||
})
|
||||
'/static': static_path})
|
||||
return app
|
||||
|
@ -14,7 +14,8 @@ from lodgeit import local
|
||||
from lodgeit.lib import antispam
|
||||
from lodgeit.i18n import list_languages, _
|
||||
from lodgeit.utils import render_to_response
|
||||
from lodgeit.database import session, Paste
|
||||
from lodgeit.models import Paste
|
||||
from lodgeit.database import db
|
||||
from lodgeit.lib.highlighting import list_languages, STYLES, get_style
|
||||
from lodgeit.lib.pagination import generate_pagination
|
||||
from lodgeit.lib.captcha import check_hashed_solution, Captcha
|
||||
@ -57,7 +58,8 @@ class PasteController(object):
|
||||
if code and language and not error:
|
||||
paste = Paste(code, language, parent, req.user_hash,
|
||||
'private' in req.form)
|
||||
session.flush()
|
||||
db.session.add(paste)
|
||||
db.session.commit()
|
||||
local.request.session['language'] = language
|
||||
return redirect(paste.url)
|
||||
|
||||
|
@ -5,190 +5,56 @@
|
||||
|
||||
Database fun :)
|
||||
|
||||
:copyright: 2007-2008 by Armin Ronacher, Christopher Grebs.
|
||||
:copyright: 2007-2010 by Armin Ronacher, Christopher Grebs.
|
||||
:license: BSD
|
||||
"""
|
||||
import time
|
||||
import difflib
|
||||
from datetime import datetime
|
||||
from werkzeug import cached_property
|
||||
from sqlalchemy import MetaData, Integer, Text, DateTime, ForeignKey, \
|
||||
String, Boolean, Table, Column, select, and_, func
|
||||
from sqlalchemy.orm import scoped_session, create_session, backref, relation
|
||||
from sqlalchemy.orm.scoping import ScopedSession
|
||||
from lodgeit import local
|
||||
from lodgeit.utils import generate_paste_hash
|
||||
from lodgeit.lib.highlighting import highlight, preview_highlight, LANGUAGES
|
||||
import sys
|
||||
from types import ModuleType
|
||||
import sqlalchemy
|
||||
from sqlalchemy import MetaData, create_engine
|
||||
from sqlalchemy import orm, sql
|
||||
from sqlalchemy.orm.session import Session
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from lodgeit.local import application, _local_manager
|
||||
|
||||
|
||||
session = scoped_session(lambda: create_session(local.application.engine),
|
||||
scopefunc=local._local_manager.get_ident)
|
||||
|
||||
metadata = MetaData()
|
||||
|
||||
pastes = Table('pastes', metadata,
|
||||
Column('paste_id', Integer, primary_key=True),
|
||||
Column('code', Text),
|
||||
Column('parent_id', Integer, ForeignKey('pastes.paste_id'),
|
||||
nullable=True),
|
||||
Column('pub_date', DateTime),
|
||||
Column('language', String(30)),
|
||||
Column('user_hash', String(40), nullable=True),
|
||||
Column('handled', Boolean, nullable=False),
|
||||
Column('private_id', String(40), unique=True, nullable=True)
|
||||
)
|
||||
|
||||
def session_factory():
|
||||
opts = {
|
||||
'autoflush': True,
|
||||
'transactional': True}
|
||||
return orm.create_session(application.engine, **opts)
|
||||
session = orm.scoped_session(
|
||||
session_factory, scopefunc=_local_manager.get_ident)
|
||||
|
||||
|
||||
class Paste(object):
|
||||
"""Represents a paste."""
|
||||
class ModelBase(object):
|
||||
"""Internal baseclass for all models. It provides some syntactic
|
||||
sugar and maps the default query property.
|
||||
|
||||
def __init__(self, code, language, parent=None, user_hash=None,
|
||||
private=False):
|
||||
if language not in LANGUAGES:
|
||||
language = 'text'
|
||||
self.code = u'\n'.join(code.splitlines())
|
||||
self.language = language
|
||||
if isinstance(parent, Paste):
|
||||
self.parent = parent
|
||||
elif parent is not None:
|
||||
self.parent_id = parent
|
||||
self.pub_date = datetime.now()
|
||||
self.handled = False
|
||||
self.user_hash = user_hash
|
||||
self.private = private
|
||||
|
||||
@staticmethod
|
||||
def get(identifier):
|
||||
"""Return the paste for an identifier. Private pastes must be loaded
|
||||
with their unique hash and public with the paste id.
|
||||
"""
|
||||
if isinstance(identifier, basestring) and not identifier.isdigit():
|
||||
return Paste.query.filter(Paste.private_id == identifier).first()
|
||||
return Paste.query.filter(
|
||||
(Paste.paste_id == int(identifier)) &
|
||||
(Paste.private_id == None)
|
||||
).first()
|
||||
|
||||
@staticmethod
|
||||
def find_all():
|
||||
"""Return a query for all public pastes ordered by the id in reverse
|
||||
order.
|
||||
"""
|
||||
return Paste.query.filter(Paste.private_id == None) \
|
||||
.order_by(Paste.paste_id.desc())
|
||||
|
||||
@staticmethod
|
||||
def count():
|
||||
"""Count all pastes."""
|
||||
s = select([func.count(pastes.c.paste_id)])
|
||||
return session.execute(s).fetchone()[0]
|
||||
|
||||
@staticmethod
|
||||
def resolve_root(identifier):
|
||||
"""Find the root paste for a paste tree."""
|
||||
paste = Paste.get(identifier)
|
||||
if paste is None:
|
||||
return
|
||||
while paste.parent_id is not None:
|
||||
paste = paste.parent
|
||||
return paste
|
||||
|
||||
@staticmethod
|
||||
def fetch_replies():
|
||||
"""Get the new replies for the ower of a request and flag them
|
||||
as handled.
|
||||
"""
|
||||
s = select([pastes.c.paste_id],
|
||||
Paste.user_hash == local.request.user_hash
|
||||
)
|
||||
|
||||
paste_list = Paste.query.filter(and_(
|
||||
Paste.parent_id.in_(s),
|
||||
Paste.handled == False,
|
||||
Paste.user_hash != local.request.user_hash,
|
||||
)).order_by(pastes.c.paste_id.desc()).all()
|
||||
|
||||
to_mark = [p.paste_id for p in paste_list]
|
||||
session.execute(pastes.update(pastes.c.paste_id.in_(to_mark),
|
||||
values={'handled': True}))
|
||||
return paste_list
|
||||
|
||||
def _get_private(self):
|
||||
return self.private_id is not None
|
||||
|
||||
def _set_private(self, value):
|
||||
if not value:
|
||||
self.private_id = None
|
||||
return
|
||||
if self.private_id is None:
|
||||
while 1:
|
||||
self.private_id = generate_paste_hash()
|
||||
paste = Paste.query.filter(Paste.private_id ==
|
||||
self.private_id).first()
|
||||
if paste is None:
|
||||
break
|
||||
private = property(_get_private, _set_private, doc='''
|
||||
The private status of the paste. If the paste is private it gets
|
||||
a unique hash as identifier, otherwise an integer.
|
||||
''')
|
||||
del _get_private, _set_private
|
||||
|
||||
@property
|
||||
def identifier(self):
|
||||
"""The paste identifier. This is a string, the same the `get`
|
||||
method accepts.
|
||||
"""
|
||||
if self.private:
|
||||
return self.private_id
|
||||
return str(self.paste_id)
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
"""The URL to the paste."""
|
||||
return '/show/%s/' % self.identifier
|
||||
|
||||
def compare_to(self, other, context_lines=4, template=False):
|
||||
"""Compare the paste with another paste."""
|
||||
udiff = u'\n'.join(difflib.unified_diff(
|
||||
self.code.splitlines(),
|
||||
other.code.splitlines(),
|
||||
fromfile='Paste #%s' % self.identifier,
|
||||
tofile='Paste #%s' % other.identifier,
|
||||
lineterm='',
|
||||
n=context_lines
|
||||
))
|
||||
if template:
|
||||
from lodgeit.lib.diff import prepare_udiff
|
||||
diff, info = prepare_udiff(udiff)
|
||||
return diff and diff[0] or None
|
||||
return udiff
|
||||
|
||||
@cached_property
|
||||
def parsed_code(self):
|
||||
"""The paste as rendered code."""
|
||||
return highlight(self.code, self.language)
|
||||
|
||||
def to_xmlrpc_dict(self):
|
||||
"""Convert the paste into a dict for XMLRCP."""
|
||||
return {
|
||||
'paste_id': self.paste_id,
|
||||
'code': self.code,
|
||||
'parsed_code': self.parsed_code,
|
||||
'pub_date': int(time.mktime(self.pub_date.timetuple())),
|
||||
'language': self.language,
|
||||
'parent_id': self.parent_id,
|
||||
'url': self.url
|
||||
}
|
||||
|
||||
def render_preview(self, num=5):
|
||||
"""Render a preview for this paste."""
|
||||
return preview_highlight(self.code, self.language, num)
|
||||
We use the declarative model api from sqlalchemy.
|
||||
"""
|
||||
|
||||
|
||||
session.mapper(Paste, pastes, properties={
|
||||
'children': relation(Paste,
|
||||
primaryjoin=pastes.c.parent_id==pastes.c.paste_id,
|
||||
cascade='all',
|
||||
backref=backref('parent', remote_side=[pastes.c.paste_id])
|
||||
)
|
||||
})
|
||||
# configure the declarative base
|
||||
Model = declarative_base(name='Model', cls=ModelBase,
|
||||
mapper=orm.mapper, metadata=metadata)
|
||||
ModelBase.query = session.query_property()
|
||||
|
||||
|
||||
def _make_module():
|
||||
db = ModuleType('db')
|
||||
for mod in sqlalchemy, orm:
|
||||
for key, value in mod.__dict__.iteritems():
|
||||
if key in mod.__all__:
|
||||
setattr(db, key, value)
|
||||
|
||||
db.session = session
|
||||
db.metadata = metadata
|
||||
db.Model = Model
|
||||
db.NoResultFound = orm.exc.NoResultFound
|
||||
return db
|
||||
|
||||
sys.modules['lodgeit.database.db'] = db = _make_module()
|
||||
|
@ -9,7 +9,8 @@
|
||||
:license: BSD.
|
||||
"""
|
||||
import inspect
|
||||
from lodgeit.database import session, Paste
|
||||
from lodgeit.models import Paste
|
||||
from lodgeit.database import db
|
||||
from lodgeit.lib.xmlrpc import XMLRPCRequestHandler
|
||||
from lodgeit.lib.json import JSONRequestHandler
|
||||
from lodgeit.lib.highlighting import STYLES, LANGUAGES, get_style, \
|
||||
@ -77,7 +78,8 @@ def pastes_new_paste(language, code, parent_id=None,
|
||||
raise ValueError('parent paste not found')
|
||||
|
||||
paste = Paste(code, language, parent, private=private)
|
||||
session.flush()
|
||||
db.session.add(paste)
|
||||
db.session.commit()
|
||||
return paste.identifier
|
||||
|
||||
|
||||
|
@ -108,7 +108,7 @@ def render_to_response(template_name, **context):
|
||||
adds the current request to the context. This is used for the
|
||||
welcome message.
|
||||
"""
|
||||
from lodgeit.database import Paste
|
||||
from lodgeit.models import Paste
|
||||
request = local.request
|
||||
if request.method == 'GET':
|
||||
context['new_replies'] = Paste.fetch_replies()
|
||||
|
12
manage.py
12
manage.py
@ -1,12 +1,10 @@
|
||||
import os
|
||||
|
||||
from werkzeug import script
|
||||
from werkzeug.serving import run_simple
|
||||
from werkzeug.utils import create_environ, run_wsgi_app
|
||||
from werkzeug import script, run_simple, create_environ, run_wsgi_app
|
||||
|
||||
from lodgeit import local
|
||||
from lodgeit.application import make_app
|
||||
from lodgeit.database import session
|
||||
from lodgeit.database import db
|
||||
|
||||
dburi = 'sqlite:////tmp/lodgeit.db'
|
||||
|
||||
@ -18,19 +16,19 @@ def run_app(app, path='/'):
|
||||
return run_wsgi_app(app, env)
|
||||
|
||||
action_runserver = script.make_runserver(
|
||||
lambda: make_app(dburi, SECRET_KEY),
|
||||
lambda: make_app(dburi, SECRET_KEY, debug=True),
|
||||
use_reloader=True)
|
||||
|
||||
action_shell = script.make_shell(
|
||||
lambda: {
|
||||
'app': make_app(dburi, SECRET_KEY, False, True),
|
||||
'local': local,
|
||||
'session': session,
|
||||
'db': db,
|
||||
'run_app': run_app
|
||||
},
|
||||
('\nWelcome to the interactive shell environment of LodgeIt!\n'
|
||||
'\n'
|
||||
'You can use the following predefined objects: app, local, session.\n'
|
||||
'You can use the following predefined objects: app, local, db.\n'
|
||||
'To run the application (creates a request) use *run_app*.')
|
||||
)
|
||||
|
||||
|
@ -16,7 +16,7 @@ def after_install(options, home_dir):
|
||||
easy_install('Jinja2', home_dir)
|
||||
easy_install('Werkzeug', home_dir)
|
||||
easy_install('Pygments', home_dir)
|
||||
easy_install('SQLAlchemy', home_dir)
|
||||
easy_install('SQLAlchemy==0.6', home_dir)
|
||||
easy_install('simplejson', home_dir)
|
||||
easy_install('Babel', home_dir)
|
||||
easy_install('PIL', home_dir)
|
||||
|
Loading…
x
Reference in New Issue
Block a user