今までのものは、これ。
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 件のコメント:
コメントを投稿