chmod a+x script.py
./script.py <>
flask\Scripts\python script.py <>
Flask-SQLAlchemy
extension. This extension provides a wrapper for the SQLAlchemy
project, which is an ORM or Object-relational mapping.SQLAlchemy-migrate
to track the database update for us. This will add a bit of work to run the database, but this is a small price to never worry about manually migrating the database.sqlite
. These databases are the most suitable choice for small applications, as each database is stored in a separate file. import os basedir = os.path.abspath(os.path.dirname(__file__)) SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
SQLALCHEMY_DATABASE_URI
required for the Flask-SQLAlchemy
extension. This is the path to the file with our database.SQLALCHEMY_MIGRATE_REPO
is the folder where we will store the SQLAlchemy-migrate.
files SQLAlchemy-migrate.
from flask import Flask from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_object('config') db = SQLAlchemy(app) from app import views, models
db
object, which will be our database, and we also import a new module called models
. We will write this module further.id
field is usually for all models, it is used as the primary key. Each user in the database will be associated with a unique id
value stored in this field. Fortunately, this is done for us automatically, we just need to provide the id
field.nickname
and email
fields are defined as strings (or VARCHAR
in database jargon), and they have a maximum length that allows our database to optimize space utilization.role
field is an integer number that we will use to keep track of which users are administrators and who are not. from app import db ROLE_USER = 0 ROLE_ADMIN = 1 class User(db.Model): id = db.Column(db.Integer, primary_key = True) nickname = db.Column(db.String(64), index = True, unique = True) email = db.Column(db.String(120), index = True, unique = True) role = db.Column(db.SmallInteger, default = ROLE_USER) def __repr__(self): return '<User %r>' % (self.nickname)
User
class we just created contains several fields defined as class variables. Fields are created as instances of the db.Column
class, which takes a field type as an argument, plus other optional arguments that allow us, for example, to specify which fields are unique and indexed.__repr__
method tells Python how to output objects of this class. We will use it for debugging.SQLAlchemy-migrate
package comes with command line tools and an API for creating databases that will allow for easy updates in the future, which we will do. I find the command line tools inconvenient to use, so instead of them I wrote my own set of small Python scripts that invoke the migrations API. #!flask/bin/python from migrate.versioning import api from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_MIGRATE_REPO from app import db import os.path db.create_all() if not os.path.exists(SQLALCHEMY_MIGRATE_REPO): api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository') api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) else: api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))
./db_create.py
app.db
file. This is an empty sqlite database that initially supports migration. You also have a db_repository
directory with several files inside. At this point, SQLAlchemy-migrate
stores its data files. I note that we do not recreate the repository, if it has already been created. This will allow us to recreate the databases from the existing repository, if necessary. #!flask/bin/python import imp from migrate.versioning import api from app import db from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_MIGRATE_REPO migration = SQLALCHEMY_MIGRATE_REPO + '/versions/%03d_migration.py' % (api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) + 1) tmp_module = imp.new_module('old_model') old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) exec old_model in tmp_module.__dict__ script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata) open(migration, "wt").write(script) api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) print 'New migration saved as ' + migration print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
SQLAlchemy-migrate
migration is to compare the structure of our database (obtained from the app.db
file) and the structure of our model (obtained from the models.py
file). The differences between them are recorded as a migration script within the repository. The migration script knows how to apply the migration or cancel it, so it will always be possible to update or “roll back” the database format.SQLAlchemy-migrate
in defining changes, I never rename existing fields, limiting changes by adding / removing models or fields, changing the types of fields created. And I always look at the generated migration script to make sure it is correct. ./db_migrate.py
New migration saved as db_repository/versions/001_migration.py Current database version: 1
#!flask/bin/python from migrate.versioning import api from config import SQLALCHEMY_DATABASE_URI from config import SQLALCHEMY_MIGRATE_REPO v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v - 1) print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))
Flask-SQLAlchemy
will help us with both types of queries.user_id
field deserves an explanation. from app import db ROLE_USER = 0 ROLE_ADMIN = 1 class User(db.Model): id = db.Column(db.Integer, primary_key = True) nickname = db.Column(db.String(64), unique = True) email = db.Column(db.String(120), unique = True) role = db.Column(db.SmallInteger, default = ROLE_USER) posts = db.relationship('Post', backref = 'author', lazy = 'dynamic') def __repr__(self): return '<User %r>' % (self.nickname) class Post(db.Model): id = db.Column(db.Integer, primary_key = True) body = db.Column(db.String(140)) timestamp = db.Column(db.DateTime) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) def __repr__(self): return '<Post %r>' % (self.body)
Post
, which will represent the blog entries written by users. The user_id
field in the Post class will be initialized as a foreign key, so Flask-SQLAlchemy
knows that this field will be associated with the user.posts
field to the User
class, which is done like a db.relationship
field. In fact, this is not a DB field, so it is not on our diagram. For one-to-many db.relationship
field db.relationship
usually defined on the “one” side. Through this link, we get a user.posts
user, which gives us a list of all user entries. The first argument, db.relationship
refers to the “many” class in this connection. The backref
argument defines a field that will be added to objects of the "many" class, pointing to the "one" object. In our case, this means that we can use post.author
to get the User
instance that created this record. Do not worry if these details do not make sense to you, we will see examples at the end of this article. ./db_migrate.py
New migration saved as db_repository/versions/002_migration.py Current database version: 2
flask/bin/python
flask\Scripts\python
>>> from app import db, models >>>
>>> u = models.User(nickname='john', email='john@email.com', role=models.ROLE_USER) >>> db.session.add(u) >>> db.session.commit() >>>
db.session.commit()
that automatically records all changes. If there is an error while working in the session, the call to db.session.rollback()
will return the database to the state before the session is started. If neither commit nor rollback are called, the system will, by default, roll back the session. Sessions ensure that the database never stays in an inconsistent state. >>> u = models.User(nickname='susan', email='susan@email.com', role=models.ROLE_USER) >>> db.session.add(u) >>> db.session.commit() >>>
>>> users = models.User.query.all() >>> print users [<User u'john'>, <User u'susan'>] >>> for u in users: ... print u.id,u.nickname ... 1 john 2 susan >>>
>>> u = models.User.query.get(1) >>> print u <User u'john'> >>>
>>> import datetime >>> u = models.User.query.get(1) >>> p = models.Post(body='my first post!', timestamp=datetime.datetime.utcnow(), author=u) >>> db.session.add(p) >>> db.session.commit()
user_id
field in the Post
class. Instead, we store a User
object inside our author
field. This virtual field was added by Flask-SQLAlchemy to help with relationships, we defined the name of this field in the backref argument in our model's db.relationship. With this information, the ORM layer will know how to fill in user_id for us. # >>> u = models.User.query.get(1) >>> print u <User u'john'> >>> posts = u.posts.all() >>> print posts [<Post u'my first post!'>] # >>> for p in posts: ... print p.id,p.author.nickname,p.body ... 1 john my first post! # >>> u = models.User.query.get(2) >>> print u <User u'susan'> >>> print u.posts.all() [] # >>> print models.User.query.order_by('nickname desc').all() [<User u'susan'>, <User u'john'>] >>>
>>> users = models.User.query.all() >>> for u in users: ... db.session.delete(u) ... >>> posts = models.Post.query.all() >>> for p in posts: ... db.session.delete(p) ... >>> db.session.commit() >>>
Source: https://habr.com/ru/post/196810/
All Articles