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