1. 程式人生 > >【Flask】cookiecutter-flask生成的database.py程式碼分析

【Flask】cookiecutter-flask生成的database.py程式碼分析

關於cookiecutter-flask

Cookiecutter可以讓你快速從模板中建立工程,cookiecutter-flask則是Flask的模板,可以快速生成Flask大型專案模板。

cookiecutter-flask生成的工程目錄下有一個database.py檔案,主要是對資料表功能的一個擴充套件。這篇文章是對這份程式碼簡單分析。上程式碼

database.py

# -*- coding: utf-8 -*-
"""Database module, including the SQLAlchemy database object and DB-related
utilities.
"""
from sqlalchemy.orm import relationship from .extensions import db from .compat import basestring # Alias common SQLAlchemy names Column = db.Column relationship = relationship class CRUDMixin(object): """Mixin that adds convenience methods for CRUD (create, read, update, delete) operations. """
@classmethod def create(cls, **kwargs): """Create a new record and save it the database.""" instance = cls(**kwargs) return instance.save() def update(self, commit=True, **kwargs): """Update specific fields of a record.""" for attr, value in kwargs.iteritems(): setattr(self, attr, value) return
commit and self.save() or self def save(self, commit=True): """Save the record.""" db.session.add(self) if commit: db.session.commit() return self def delete(self, commit=True): """Remove the record from the database.""" db.session.delete(self) return commit and db.session.commit() class Model(CRUDMixin, db.Model): """Base model class that includes CRUD convenience methods.""" __abstract__ = True # From Mike Bayer's "Building the app" talk # https://speakerdeck.com/zzzeek/building-the-app class SurrogatePK(object): """A mixin that adds a surrogate integer 'primary key' column named ``id`` to any declarative-mapped class. """ __table_args__ = {'extend_existing': True} id = db.Column(db.Integer, primary_key=True) @classmethod def get_by_id(cls, id): if any( (isinstance(id, basestring) and id.isdigit(), isinstance(id, (int, float))), ): return cls.query.get(int(id)) return None def ReferenceCol(tablename, nullable=False, pk_name='id', **kwargs): """Column that adds primary key foreign key reference. Usage: :: category_id = ReferenceCol('category') category = relationship('Category', backref='categories') """ return db.Column( db.ForeignKey("{0}.{1}".format(tablename, pk_name)), nullable=nullable, **kwargs)

CRUDMixin擁有四個方法,分別是createupdatesavedelete。可以讓資料表物件一步操作CURD。

Model分別繼承CRUDMixin和SQLAchemy的db.Model,後面的資料表物件只需要繼承Model就同時繼承了CRUDMixin方法了。

SurrogatePK是為資料表新增主鍵欄位。繼承SurrogatePK即可。

ReferenceCol是外來鍵引用,封裝了db.ForeignKey的操作。

資料表

下面放上User和Role的資料表,該User和Role是一對多關係。

class User(UserMixin, SurrogatePK, Model):

    __tablename__ = 'users'
    username = Column(db.String(80), unique=True, nullable=False)
    email = Column(db.String(80), unique=True, nullable=False)
    #: 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, 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):
        return "{0} {1}".format(self.first_name, self.last_name)

    def __repr__(self):
        return '<User({username!r})>'.format(username=self.username)

class Role(SurrogatePK, Model):
    __tablename__ = 'roles'
    name = Column(db.String(80), unique=True, nullable=False)
    user_id = ReferenceCol('users', nullable=True)
    user = relationship('User', backref='roles')

    def __init__(self, name, **kwargs):
        db.Model.__init__(self, name=name, **kwargs)

    def __repr__(self):
        return '<Role({name})>'.format(name=self.name)

另外注意一點的是,要使資料表支援Flask-Admin,需要把__init__的引數設一個預設值。比如USER的
def __init__(self, username, email, password=None, **kwargs)
要修改為
def __init__(self, username="", email="", password=None, **kwargs)

否則Flask-Admin編輯時會報錯。