今までのものは、これ。
Pyramid 1.5 + jinja2-alchemy-starter で pcreate
Pyramid 1.5 + jinja2-alchemy-starter で @view_config()
Pyramid 1.5 + jinja2-alchemy-starter で authentication
前回はログインするページをつくりました。
ログイン ID とパスワードはソフト埋め込みでしたが、今回はデータベースに格納した ID とパスワードを使うことにします。
データベースはひとまず、標準で入っている sqlite を使います。次回は MySQL にします。
今のところ、development.ini に次の記述があります。sqlite のファイル名を指定しています。
sqlalchemy.url = sqlite:///%(here)s/MyProject.sqlite
あとは、myproject/models.py と myproject/security.py が関係しています。
models.py では・・・
class MyModel(Base) は不要ですね。
class User(Base): これがあれば良いと思われます。テーブルの名称は users で、フィールドは primary key の id と unique な name、それに password です。group も必要?
class RootFactory(object): は、__init__.py の Configurator() で使われていて、非常に重要っぽいものなので、そのままにしておきます。
security.py は・・・
group が editors と viewers があって、editors には editor が、viewers には viewer という名前の user がいます。
def groupfinder(userid, request): では、userid が属す group を返しています。
この部分を、sqlite から持ってくるようにすれば良さそうです。
というわけで、models.py の users テーブルは id、name、password のほかに group フィールドを持たせることにします。
models.py はこうなりました。User に group を追加しました。
#!/usr/bin/env python # coding: UTF-8 from pyramid.security import ( Allow, Everyone, ) from sqlalchemy import ( Column, Integer, Text, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import ( scoped_session, sessionmaker, ) from zope.sqlalchemy import ZopeTransactionExtension DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base() class User(Base): """ The SQLAlchemy declarative model class for a User object. """ __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(Text, unique=True) password = Column(Text) group = Column(Text) def __init__(self, name, password, group): self.name = name self.password = password self.group = group class RootFactory(object): __acl__ = [ (Allow, Everyone, 'view'), (Allow, 'group:editors', 'edit') ] def __init__(self, request): pass def initialize_sql(engine): DBSession.configure(bind=engine)
この辺りを参考にします。
http://wiki.liris.org/article/python_intro/python03
http://seesaawiki.jp/w/kurt0027/d/python%20sqlalchemy%A4%CD%A4%BF
http://bty.sakura.ne.jp/wp/archives/634
development.ini にあるデータベースのファイル名を返す関数を持つ my_util.py を追加します。ちょっといい加減な関数です。
#!/usr/bin/env python # coding: UTF-8 import pyramid class MyUtil(): def get_db_name(self): db_name = pyramid.threadlocal.get_current_registry().settings db_name = db_name['sqlalchemy.url'] db_name = db_name[10:] return db_name
security.py はこうなりました。
#!/usr/bin/env python # coding: UTF-8 import sqlite3 import sqlalchemy import myproject.my_util class myproject_db(): def __init__(self, fname): self.con = None self.fname = fname if fname is not None: self.con = sqlite3.connect(fname) # , isolation_level=None no_db = True try: cur = self.con.cursor() sql = 'SELECT name, password, user_group FROM users WHERE user_group=?' cur.execute(sql, (u'editor',)) no_db = False except Exception as ex: print(ex) self.con.close() if no_db: # Create table self.con = sqlite3.connect(fname) cur = self.con.cursor() sql = '''create table users ( id integer primary key autoincrement not null, name text, password text, user_group text)''' cur.execute(sql) tp = ('editor', 'editor', 'editors',) cur.execute(''' INSERT INTO users (name, password, user_group) VALUES (?, ?, ?)''', tp) self.con.commit() cur.close() def connect(self): if self.fname is not None: self.con = sqlite3.connect(self.fname) else: self.con = None return self.con def get_group_and_password_of_user(self, user): group = None password = None self.connect() if self.con is not None: try: cur = self.con.cursor() tp = (user,) cur.execute('SELECT user_group, password FROM users WHERE name=?', tp) for row in cur: group = row[0] password = row[1] cur.close() except Exception as ex: print(ex) group = None password = None self.con.close() return (group, password) def put_group_and_password_of_user(self, user, group, password): sql = 'UPDATE users SET user_group=?, password=? WHERE name=?;' self.connect() if self.con is not None: try: cur = self.con.cursor() tp = (group, password, user,) cur.execute(sql, tp) self.con.commit() cur.close() except Exception as ex: print(ex) self.con.close() def groupfinder(userid, request): my_util = myproject.my_util.MyUtil() db_name = my_util.get_db_name() db = myproject_db(db_name) (group, password) = db.get_group_and_password_of_user(userid) the_str = None # ['group:editors'] if group is not None: the_str = 'group:' + group return [the_str] else: return [] if __name__ == '__main__': db_name = 'MyProject.sqlite' db = myproject_db(db_name) db.connect() if db.con is not None: try: cur = db.con.cursor() sql = 'SELECT name, password, user_group FROM users ORDER BY name;' cur.execute(sql) for xx in cur.fetchall(): print(xx[0], xx[1], xx[2]) cur.close() except Exception as ex: print(ex) db.con.close()
views.py もかなり修正しました。
#!/usr/bin/env python # coding: UTF-8 from pyramid.i18n import TranslationStringFactory from myproject.models import DBSession from pyramid.httpexceptions import HTTPFound from pyramid.response import Response from pyramid.view import ( view_config, forbidden_view_config, ) from pyramid.security import ( remember, forget, authenticated_userid, ) import myproject.security import myproject.my_util _ = TranslationStringFactory('MyProject') @view_config(route_name='home', renderer='home.html', permission='edit', request_method="GET") def home(request): page_name = 'home' results = { 'page_name': page_name, 'next_uri': '/next', 'next_name': 'next_page' } return results @view_config(route_name='next_page', renderer='home.html', permission='edit', request_method="GET") def next_page(request): page_name = 'next_page' results = { 'page_name': page_name, 'next_uri': '/', 'next_name': 'home' } return results @view_config(route_name='login', renderer='login.html') @forbidden_view_config(renderer='login.html') def login(request): login_url = request.route_url('login') referrer = request.url if referrer == login_url: referrer = '/' # never use the login form itself as came_from came_from = request.params.get('came_from', referrer) message = '' login = '' password = '' if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] # if USERS.get(login) == password: my_util = myproject.my_util.MyUtil() db_name = my_util.get_db_name() my_db = myproject.security.myproject_db(db_name) (group, pwd) = my_db.get_group_and_password_of_user(login) if pwd == password: headers = remember(request, login) return HTTPFound(location = came_from, headers = headers) message = 'Failed login' return dict( message = message, url = request.application_url + '/login', came_from = came_from, login = login, password = password, ) @view_config(route_name='logout') def logout(request): headers = forget(request) return HTTPFound(location = request.route_url('home'), headers = headers)
最後にテンプレートの login.html。
ログインエラーなどのメッセージを表示するように修正しました。
<!DOCTYPE html> <html> <head> <title>Login</title> <meta charset="utf-8"> <link rel="stylesheet" href="{{request.application_url}}/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> </head> <body> <form action="{{ url }}" method="post" id="id_login_form"> <input type="text" name="login" value="{{ login }}" placeholder="login name" autofocus required/><br/> <input type="password" name="password" value="{{ password }}" placeholder="password" required /><br/> <input type="hidden" name="came_from" value="{{ came_from }}" /> <input type="submit" name="form.submitted" value="Log In" /> </form> {{ message }} </body> </html>
これで修正は終了です。
実行すると動きは変わりありませんが、MyProject.sqlite ファイルが作成されているのがわかります。
MyProject.sqlite が置かれているディレクトリで myproject/security.py を実行すると MyProject.sqlite の中を表示します。
$ ../bin/python myproject/security.py (u'editor', u'editor', u'editors') $
0 件のコメント:
コメントを投稿