Quick Start¶
Install¶
pip install chemist
MariaDB/MySQL¶
pip install chemist[mysql]
pip install chemist[mariadb] # alias to [mysql]
Postgres¶
pip install chemist[psycopg2]
pip install chemist[postgres] # alias to [psycopg2]
pip install chemist[postgresql] # alias to [psycopg2]
Declaring a model¶
import bcrypt
from chemist import (
Model, db, metadata
set_default_uri,
)
engine = set_default_uri('sqlite:///example.db')
CREDIT_CARD_ENCRYPTION_KEY = b'\xb3\x0f\xcc9\xc3\xb1k#\x95j4\xb3\x1f\x08\x98\xd7~6\xff\xceb\xdc\x17vW\xd7\x90\xcf\x82\x9d\xb7j'
class User(Model):
table = db.Table(
'auth_user',
metadata,
db.Column('id', db.Integer, primary_key=True),
db.Column('email', db.String(100), nullable=False, unique=True),
db.Column('password', db.String(100), nullable=False, unique=True),
db.Column('created_at', db.DateTime, default=datetime.now),
db.Column('updated_at', db.DateTime, default=datetime.now),
db.Column('credit_card', db.String(16)),
)
encryption = {
# transparently encrypt data data in "credit_card" field before storing on DB
# also transparently decrypt after retrieving data
'credit_card': CREDIT_CARD_ENCRYPTION_KEY,
}
@classmethod
def create(cls, email, password, **kw):
email = email.lower()
password = cls.secretify_password(password)
return super(User, cls).create(email=email, password=password, **kw)
def to_dict(self):
# prevent password and credit-card to be returned in HTTP responses that serialize model data
data = self.serialize()
data.pop('password', None)
data.pop('credit_card', None)
return data
@classmethod
def secretify_password(cls, plain):
return bcrypt.hashpw(plain, bcrypt.gensalt(12))
def match_password(self, plain):
return self.password == bcrypt.hashpw(plain, self.password)
def change_password(self, old_password, new_password):
right_password = self.match_password(old_password)
if right_password:
secret = self.secretify_password(new_password)
self.set(password=secret)
self.save()
return True
return False
metadata.drop_all()
metadata.create_all()
Creating new records¶
data = {
"email": "octocat@github.com",
"password": "1234",
}
created = User.create(**data)
assert created.id == 1
assert created.to_dict() == {
'id': 1,
}
same_user = User.get_or_create(**data)
assert same_user.id == created.id
Querying¶
user_count = User.count()
user_list = User.all()
github_users = User.find_by(email__contains='github.com')
octocat = User.find_one_by(email='octocat@github.com')
assert octocat == user_list[0]
assert octocat.id == 1
assert user_count == 1
Editing active records¶
octocat = User.find_one_by(email='octocat@github.com')
# modify in memory
octocat.password = 'much more secure'
# or ...
octocat.set(
password='much more secure',
email='octocat@gmail.com',
)
# save changes (commit transaction and flush db session)
octocat.save()
# or ...
# modify and save changes in a single call
saved_cat = octocat.update_and_save(
password='even more secure now',
email='octocat@protonmail.com',
)
assert saved_cat == octocat
Deleting¶
from chemist import set_default_uri
engine = set_default_uri('sqlite:///example.db')
octocat = User.find_one_by(email='octocat@github.com')
# delete row, commit and flush session
ghost_cat = octocat.delete()
# but the copy in memory still has all the data
assert ghost_cat.id == 1
# resurrecting the cat
octocat = ghost_cat.save()