Revert User#set_password and User#check_password for Migrate compatibility

master
Steven Loria 11 years ago
parent 38adf64997
commit 923b1f55e2
  1. 10
      {{cookiecutter.app_name}}/manage.py
  2. 1
      {{cookiecutter.app_name}}/tests/conftest.py
  3. 5
      {{cookiecutter.app_name}}/tests/factories.py
  4. 22
      {{cookiecutter.app_name}}/tests/test_models.py
  5. 1
      {{cookiecutter.app_name}}/tests/test_webtests.py
  6. 51
      {{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py
  7. 2
      {{cookiecutter.app_name}}/{{cookiecutter.app_name}}/public/forms.py
  8. 17
      {{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py

@ -20,19 +20,19 @@ manager = Manager(app)
TEST_CMD = "py.test tests"
def _make_context():
'''Return context dict for a shell session so you can access
"""Return context dict for a shell session so you can access
app, db, and the User model by default.
'''
"""
return {'app': app, 'db': db, 'User': User}
@manager.command
def test():
'''Run the tests.'''
"""Run the tests."""
status = subprocess.call(TEST_CMD, shell=True)
sys.exit(status)
manager.add_command("server", Server())
manager.add_command("shell", Shell(make_context=_make_context))
manager.add_command('server', Server())
manager.add_command('shell', Shell(make_context=_make_context))
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':

@ -9,7 +9,6 @@ from {{ cookiecutter.app_name }}.settings import TestConfig
from {{cookiecutter.app_name}}.app import create_app
from {{cookiecutter.app_name}}.database import db as _db
from .factories import ALL_FACTORIES
@pytest.yield_fixture(scope='session')
def app():

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from factory import Sequence
from factory import Sequence, PostGenerationMethodCall
from factory.alchemy import SQLAlchemyModelFactory
from {{cookiecutter.app_name}}.user.models import User
@ -24,7 +24,6 @@ class UserFactory(BaseFactory):
username = Sequence(lambda n: "user{0}".format(n))
email = Sequence(lambda n: "user{0}@example.com".format(n))
password = 'example'
password = PostGenerationMethodCall('set_password', 'example')
active = True
ALL_FACTORIES = [UserFactory]

@ -5,42 +5,42 @@ import datetime as dt
import pytest
from {{ cookiecutter.app_name }}.user.models import User, Role
from .base import DbTestCase
from .factories import UserFactory
@pytest.mark.usefixtures('db')
class TestUser:
def test_created_at_defaults_to_datetime(self, db):
def test_created_at_defaults_to_datetime(self):
user = User(username='foo', email='foo@bar.com')
user.save()
assert bool(user.created_at)
assert isinstance(user.created_at, dt.datetime) is True
assert isinstance(user.created_at, dt.datetime)
def test_password_is_nullable(self, db):
def test_password_is_nullable(self):
user = User(username='foo', email='foo@bar.com')
user.save()
assert user.password is None
def test_factory(self, db):
def test_factory(self):
user = UserFactory(password="myprecious")
assert bool(user.username)
assert bool(user.email)
assert bool(user.created_at)
assert user.is_admin is False
assert user.active is True
assert user.password == "myprecious"
assert user.check_password('myprecious')
def test_check_password_with_equality_operators(self, db):
def test_check_password(self):
user = User.create(username="foo", email="foo@bar.com",
password="foobarbaz123")
assert user.password == 'foobarbaz123'
assert user.password != "barfoobaz"
assert user.check_password('foobarbaz123') is True
assert user.check_password("barfoobaz") is False
def test_full_name(self, db):
def test_full_name(self):
user = UserFactory(first_name="Foo", last_name="Bar")
assert user.full_name == "Foo Bar"
def test_roles(self, db):
def test_roles(self):
role = Role(name='admin')
role.save()
u = UserFactory()

@ -8,7 +8,6 @@ from flask import url_for
from {{cookiecutter.app_name}}.user.models import User
from .base import DbTestCase
from .factories import UserFactory
@pytest.fixture

@ -3,9 +3,8 @@
utilities.
"""
from sqlalchemy.orm import relationship
from sqlalchemy.types import TypeDecorator
from .extensions import db, bcrypt
from .extensions import db
Column = db.Column
relationship = relationship
@ -51,56 +50,10 @@ class CRUDMixin(object):
db.session.delete(self)
return commit and db.session.commit()
# From Mike Bayer's "atmcraft" example app
# From Mike Bayer's "Building the app" talk
# https://speakerdeck.com/zzzeek/building-the-app
def ReferenceCol(tablename, nullable=False, **kwargs):
"""Column that adds primary key foreign key reference."""
return db.Column(
db.ForeignKey("{0}.id".format(tablename)),
nullable=nullable, **kwargs)
class Password(str):
"""Coerce a string to a bcrypt password.
Rationale: for an easy string comparison,
so we can say ``some_password == 'hello123'``
.. seealso::
https://pypi.python.org/pypi/bcrypt/
"""
def __new__(cls, value, crypt=True):
if value is None:
return None
if isinstance(value, unicode):
value = value.encode('utf-8')
if crypt:
value = bcrypt.generate_password_hash(value)
return str.__new__(cls, value)
def __eq__(self, other):
if other and not isinstance(other, Password):
return bcrypt.check_password_hash(self, other)
return str.__eq__(self, other)
def __ne__(self, other):
return not self.__eq__(other)
class BcryptType(TypeDecorator):
"""Coerce strings to bcrypted Password objects for the database.
"""
impl = db.String(128)
def process_bind_param(self, value, dialect):
return Password(value)
def process_result_value(self, value, dialect):
# already crypted, so don't crypt again
return Password(value, crypt=False)
def __repr__(self):
return "BcryptType()"

@ -22,7 +22,7 @@ class LoginForm(Form):
self.username.errors.append("Unknown username")
return False
if self.user != self.password.data:
if not self.user.check_password(self.password.data):
self.password.errors.append("Invalid password")
return False

@ -3,13 +3,13 @@ import datetime as dt
from flask.ext.login import UserMixin
from {{cookiecutter.app_name}}.extensions import bcrypt
from {{cookiecutter.app_name}}.database import (
db,
CRUDMixin,
ReferenceCol,
relationship,
Column,
BcryptType,
)
@ -27,15 +27,26 @@ class User(UserMixin, CRUDMixin, db.Model):
__tablename__ = 'users'
username = Column(db.String(80), unique=True, nullable=False)
email = Column(db.String(80), unique=True, nullable=False)
password = Column(BcryptType, nullable=True)
#: The hashed password
password = Column(db.String(128), nullable=True)
created_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow)
first_name = Column(db.String(30), nullable=True)
last_name = Column(db.String(30), nullable=True)
active = Column(db.Boolean(), default=False)
is_admin = Column(db.Boolean(), default=False)
def __init__(self, username, email, **kwargs):
def __init__(self, username, email, password=None, **kwargs):
db.Model.__init__(self, username=username, email=email, **kwargs)
if password:
self.set_password(password)
else:
self.password = None
def set_password(self, password):
self.password = bcrypt.generate_password_hash(password)
def check_password(self, value):
return bcrypt.check_password_hash(self.password, value)
@property
def full_name(self):

Loading…
Cancel
Save