2012-05-20T07:30:21Z

The Flask Mega-Tutorial, Part IV: Database

(Great news! There is a new version of this tutorial!)

This is the fourth article in the series in which I document my experience writing web applications in Python using the Flask microframework.

The goal of the tutorial series is to develop a decently featured microblogging application that demonstrating total lack of originality I have decided to call microblog.

NOTE: This article was revised in September 2014 to be in sync with current versions of Python and Flask.

Here is an index of all the articles in the series that have been published to date:

Recap

In the previous chapter of the series we created our login form, complete with submission and validation.

In this article we are going to create our database and set it up so that we can record our users in it.

To follow this chapter along you need to have the microblog app as we left it at the end of the previous chapter. Please make sure the app is installed and running.

Running Python scripts from the command line

In this chapter we are going to write a few scripts that simplify the management of our database. Before we get into that let's review how a Python script is executed on the command line.

If you are on Linux or OS X, then scripts have to be given executable permission, like this:

$ chmod a+x script.py

The script has a shebang line, which points to the interpreter that should be used. A script that has been given executable permission and has a shebang line can be executed simply like this:

./script.py <arguments>

On Windows, however, this does not work, and instead you have to provide the script as an argument to the chosen Python interpreter:

$ flask\Scripts\python script.py <arguments>

To avoid having to type the path to the Python interpreter you can add your microblog/flask/Scripts directory to the system path, making sure it appears before your regular Python interpreter. This can be temporarily achieved by activating the virtual environment with the following command:

$ flask\Scripts\activate

From now on, in this tutorial the Linux/OS X syntax will be used for brevity. If you are on Windows remember to convert the syntax appropriately.

Databases in Flask

We will use the Flask-SQLAlchemy extension to manage our application. This extension provides a wrapper for the SQLAlchemy project, which is an Object Relational Mapper or ORM.

ORMs allow database applications to work with objects instead of tables and SQL. The operations performed on the objects are translated into database commands transparently by the ORM. Knowing SQL can be very helpful when working with ORMs, but we will not be learning SQL in this tutorial, we will let Flask-SQLAlchemy speak SQL for us.

Migrations

Most database tutorials I've seen cover creation and use of a database, but do not adequately address the problem of updating a database as the application grows. Typically you end up having to delete the old database and create a new one each time you need to make updates, losing all the data. And if the data cannot be recreated easily you may be forced to write export and import scripts yourself.

Luckily, we have a much better option.

We are going to use SQLAlchemy-migrate to keep track of database updates for us. It adds a bit of work to get a database started, but that is a small price to pay for never having to worry about manual database migrations.

Enough theory, let's get started!

Configuration

For our little application we will use a sqlite database. The sqlite databases are the most convenient choice for small applications, as each database is stored in a single file and there is no need to start a database server.

We have a couple of new configuration items to add to our config file (file config.py):

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')

The SQLALCHEMY_DATABASE_URI is required by the Flask-SQLAlchemy extension. This is the path of our database file.

The SQLALCHEMY_MIGRATE_REPO is the folder where we will store the SQLAlchemy-migrate data files.

Finally, when we initialize our app we also need to initialize our database. Here is our updated package init file (file app/__init__.py):

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)

from app import views, models

Note the two changes we have made to our init script. We are now creating a db object that will be our database, and we are also importing a new module called models. We will write this module next.

The database model

The data that we will store in our database will be represented by a collection of classes that are referred to as the database models. The ORM layer will do the translations required to map objects created from these classes into rows in the proper database table.

Let's start by creating a model that will represent our users. Using the WWW SQL Designer tool, I have made the following diagram to represent our users table:

users table

The id field is usually in all models, and is used as the primary key. Each user in the database will be assigned a unique id value, stored in this field. Luckily this is done automatically for us, we just need to provide the id field.

The nickname and email fields are defined as strings (or VARCHAR in database jargon), and their maximum lengths are specified so that the database can optimize space usage.

Now that we have decided what we want our users table to look like, the job of translating that into code is pretty easy (file app/models.py):

from app import db

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)

    def __repr__(self):
        return '<User %r>' % (self.nickname)

The User class that we just created contains several fields, defined as class variables. Fields are created as instances of the db.Column class, which takes the field type as an argument, plus other optional arguments that allow us, for example, to indicate which fields are unique and indexed.

The __repr__ method tells Python how to print objects of this class. We will use this for debugging.

Creating the database

With the configuration and model in place we are now ready to create our database file. The SQLAlchemy-migrate package comes with command line tools and APIs to create databases in a way that allows easy updates in the future, so that is what we will use. I find the command line tools a bit awkward to use, so instead I have written my own set of little Python scripts that invoke the migration APIs.

Here is a script that creates the database (file db_create.py):

#!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))

Note how this script is completely generic. All the application specific pathnames are imported from the config file. When you start your own project you can just copy the script to the new app's directory and it will work right away.

To create the database you just need to execute this script (remember that if you are on Windows the command is slightly different):

./db_create.py

After you run the command you will have a new app.db file. This is an empty sqlite database, created from the start to support migrations. You will also have a db_repository directory with some files inside. This is the place where SQLAlchemy-migrate stores its data files. Note that we do not regenerate the repository if it already exists. This will allow us to recreate the database while leaving the existing repository if we need to.

Our first migration

Now that we have defined our model, we can incorporate it into our database. We will consider any changes to the structure of the app database a migration, so this is our first, which will take us from an empty database to a database that can store users.

To generate a migration I use another little Python helper script (file db_migrate.py):

#!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
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
migration = SQLALCHEMY_MIGRATE_REPO + ('/versions/%03d_migration.py' % (v+1))
tmp_module = imp.new_module('old_model')
old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
exec(old_model, 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)
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print('New migration saved as ' + migration)
print('Current database version: ' + str(v))

The script looks complicated, but it doesn't really do much. The way SQLAlchemy-migrate creates a migration is by comparing the structure of the database (obtained in our case from file app.db) against the structure of our models (obtained from file app/models.py). The differences between the two are recorded as a migration script inside the migration repository. The migration script knows how to apply a migration or undo it, so it is always possible to upgrade or downgrade a database format.

While I have never had problems generating migrations automatically with the above script, I could see that sometimes it would be hard to determine what changes were made just by comparing the old and the new format. To make it easy for SQLAlchemy-migrate to determine the changes I never rename existing fields, I limit my changes to adding or removing models or fields, or changing types of existing fields. And I always review the generated migration script to make sure it is right.

It goes without saying that you should never attempt to migrate your database without having a backup, in case something goes wrong. Also never run a migration for the first time on a production database, always make sure the migration works correctly on a development database.

So let's go ahead and record our migration:

$ ./db_migrate.py

And the output from the script will be:

New migration saved as db_repository/versions/001_migration.py
Current database version: 1

The script shows where the migration script was stored, and also prints the current database version. The empty database version was version 0, after we migrated to include users we are at version 1.

Database upgrades and downgrades

By now you may be wondering why is it that important to go through the extra hassle of recording database migrations.

Imagine that you have your application in your development machine, and also have a copy deployed to a production server that is online and in use.

Let's say that for the next release of your app you have to introduce a change to your models, for example a new table needs to be added. Without migrations you would need to figure out how to change the format of your database, both in your development machine and then again in your server, and this could be a lot of work.

If you have database migration support, then when you are ready to release the new version of the app to your production server you just need to record a new migration, copy the migration scripts to your production server and run a simple script that applies the changes for you. The database upgrade can be done with this little Python script (file db_upgrade.py):

#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print('Current database version: ' + str(v))

When you run the above script, the database will be upgraded to the latest revision, by applying the migration scripts stored in the database repository.

It is not a common need to have to downgrade a database to an old format, but just in case, SQLAlchemy-migrate supports this as well (file db_downgrade.py):

#!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)
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print('Current database version: ' + str(v))

This script will downgrade the database one revision. You can run it multiple times to downgrade several revisions.

Database relationships

Relational databases are good at storing relations between data items. Consider the case of a user writing a blog post. The user will have a record in the users table, and the post will have a record in the posts table. The most efficient way to record who wrote a given post is to link the two related records.

Once a link between a user and a post is established there are two types of queries that we may need to use. The most trivial one is when you have a blog post and need to know what user wrote it. A more complex query is the reverse of this one. If you have a user, you may want to know all the posts that the user wrote. Flask-SQLAlchemy will help us with both types of queries.

Let's expand our database to store posts, so that we can see relationships in action. For this we go back to our database design tool and create a posts table:

users table

Our posts table will have the required id, the body of the post and a timestamp. Not much new there. But the user_id field deserves an explanation.

We said we wanted to link users to the posts that they write. The way to do that is by adding a field to the post that contains the id of the user that wrote it. This id is called a foreign key. Our database design tool shows foreign keys as a link between the foreign key and the id field of the table it refers to. This kind of link is called a one-to-many relationship, one user writes many posts.

Let's modify our models to reflect these changes (app/models.py):

from app import db

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)
    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)

We have added the Post class, which will represent blog posts written by users. The user_id field in the Post class was initialized as a foreign key, so that Flask-SQLAlchemy knows that this field will link to a user.

Note that we have also added a new field to the User class called posts, that is constructed as a db.relationship field. This is not an actual database field, so it isn't in our database diagram. For a one-to-many relationship a db.relationship field is normally defined on the "one" side. With this relationship we get a user.posts member that gets us the list of posts from the user. The first argument to db.relationship indicates the "many" class of this relationship. The backref argument defines a field that will be added to the objects of the "many" class that points back at the "one" object. In our case this means that we can use post.author to get the User instance that created a post. Don't worry if these details don't make much sense just yet, we'll see examples of this at the end of this article.

Let's record another migration with this change. Simply run:

$ ./db_migrate.py

And the script will respond:

New migration saved as db_repository/versions/002_migration.py
Current database version: 2

It isn't really necessary to record each little change to the database model as a separate migration, a migration is normally only recorded at significant points in the history of the project. We are doing more migrations than necessary here only to show how the migration system works.

Play time

We have spent a lot of time defining our database, but we haven't seen how it works yet. Since our app does not have database code yet let's make use of our brand new database in the Python interpreter.

So go ahead and fire up Python. On Linux or OS X:

flask/bin/python

Or on Windows:

flask\Scripts\python

Once in the Python prompt enter the following:

>>> from app import db, models
>>>

This brings our database and models into memory.

Let's create a new user:

>>> u = models.User(nickname='john', email='john@email.com')
>>> db.session.add(u)
>>> db.session.commit()
>>>

Changes to a database are done in the context of a session. Multiple changes can be accumulated in a session and once all the changes have been registered you can issue a single db.session.commit(), which writes the changes atomically. If at any time while working on a session there is an error, a call to db.session.rollback() will revert the database to its state before the session was started. If neither commit nor rollback are issued then the system by default will roll back the session. Sessions guarantee that the database will never be left in an inconsistent state.

Let's add another user:

>>> u = models.User(nickname='susan', email='susan@email.com')
>>> db.session.add(u)
>>> db.session.commit()
>>>

Now we can query what our users are:

>>> users = models.User.query.all()
>>> users
[<User u'john'>, <User u'susan'>]
>>> for u in users:
...     print(u.id,u.nickname)
...
1 john
2 susan
>>>

For this we have used the query member, which is available in all model classes. Note how the id member was automatically set for us.

Here is another way to do queries. If we know the id of a user we can find the data for that user as follows:

>>> u = models.User.query.get(1)
>>> u
<User u'john'>
>>>

Now let's add a blog post:

>>> 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()

Here we set our timestamp in UTC time zone. All timestamps stored in our database will be in UTC. We can have users from all over the world writing posts and we need to use uniform time units. In a future tutorial we will see how to show these times to users in their local timezone.

You may have noticed that we have not set the user_id field of the Post class. Instead, we are storing a User object inside the author field. The author field is a virtual field that was added by Flask-SQLAlchemy to help with relationships, we have defined the name of this field in the backref argument to db.relationship in our model. With this information the ORM layer will know how to complete the user_id for us.

To complete this session, let's look at a few more database queries that we can do:

# get all posts from a user
>>> u = models.User.query.get(1)
>>> u
<User u'john'>
>>> posts = u.posts.all()
>>> posts
[<Post u'my first post!'>]

# obtain author of each post
>>> for p in posts:
...     print(p.id,p.author.nickname,p.body)
...
1 john my first post!

# a user that has no posts
>>> u = models.User.query.get(2)
>>> u
<User u'susan'>
>>> u.posts.all()
[]

# get all users in reverse alphabetical order
>>> models.User.query.order_by('nickname desc').all()
[<User u'susan'>, <User u'john'>]
>>>

The Flask-SQLAlchemy documentation is the best place to learn about the many options that are available to query the database.

Before we close, let's erase the test users and posts we have created, so that we can start from a clean database in the next chapter:

>>> 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()
>>>

Final words

This was a long tutorial. We have learned the basics of working with a database, but we haven't incorporated the database into our application yet. In the next chapter we will put all we have learned about databases into practice when we look at our user login system.

In the meantime, if you haven't been writing the application along, you may want to download it in its current state:

Download microblog-0.4.zip.

Note that I have not included a database in the zip file above, but the repository with the migrations is there. To create a new database just use the db_create.py script, then use db_upgrade.py to upgrade the database to the latest revision.

I hope to see you next time!

Miguel

406 comments

  • #351 John Boyd said 2015-02-11T00:39:25Z

    Hello,

    First, thanks for the tutorial, I am enjoying it, I just ordered your book on Amazon because the tutorial.

    I decided to try the database scripts with a postgresql database instead, my config.py looks like:

    import os basedir = os.path.abspath(os.path.dirname(file))

    SQLALCHEMY_DATABASE_URI = 'postgresql://john:@localhost/bejois' SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')

    MY db_create.py looks like:

    !/usr/bin/env 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))

    Unfortunately my db_create.py gives the following error:

    (venv_ctax)john@john-Inspiron-3521:~/ctax$ ./db_create.py Traceback (most recent call last): File "./db_create.py", line 13, in api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO)) File "/home/john/venv_ctax/local/lib/python2.7/site-packages/migrate/versioning/api.py", line 133, in version repo = Repository(repository) File "/home/john/venv_ctax/local/lib/python2.7/site-packages/migrate/versioning/repository.py", line 77, in init self.verify(path) File "/home/john/venv_ctax/local/lib/python2.7/site-packages/migrate/versioning/repository.py", line 98, in verify raise exceptions.InvalidRepositoryError(path) migrate.exceptions.InvalidRepositoryError: /home/john/ctax/db_repository (

    my requirements.txt file looks like:

    blinker==1.3 decorator==3.4.0 Flask==0.10.1 Flask-Login==0.2.11 Flask-Script==2.0.5 Flask-SQLAlchemy==2.0 Flask-WhooshAlchemy==0.56 Flask-WTF==0.11 itsdangerous==0.24 Jinja2==2.7.3 Mako==1.0.1 MarkupSafe==0.23 pbr==0.10.7 psycopg2==2.6 six==1.9.0 SQLAlchemy==0.9.8 sqlalchemy-migrate==0.9.4 sqlparse==0.1.14 Tempita==0.5.2 Werkzeug==0.10.1 Whoosh==2.6.0 WTForms==2.0.2

    Any idea why I am getting the error(s) note that the table is created in the database.

    Best regards,

    John.

  • #352 Miguel Grinberg said 2015-02-11T01:02:35Z

    @Thomas: This is probably a limitation of the automatic migrations. I really don't remember how good or bad sqlalchemy-migrate handled these. These days I'm using Alembic, which gets most constraints. But still, I always review the generated migration scripts, and often have to make minor edits to them so that they are accurate.

  • #353 Miguel Grinberg said 2015-02-11T01:09:04Z

    @John: my guess is that your db_repository folder is corrupted, there must be something in there that sqlalchemy-migrate does not like. If you are going to work with the book, then be aware that these days I'm using Flask-Migrate to track migrations, I do not use the techniques presented in this tutorial anymore.

  • #354 John Boyd said 2015-02-11T02:46:50Z

    Thanks for the quick reply,

    I will check out Flask-Migrate.

  • #355 Chris said 2015-02-11T20:16:31Z

    Hello,

    I don't know if this thread is still alive, but, I just found it. Thanks for this tutorial, it is fantastic.

    Perhaps you can help me with the database section - how would I convert this to use an existing MS SQL database?

    Thanks, in advance.

  • #356 Miguel Grinberg said 2015-02-12T00:40:15Z

    @Chris: working with pre-existing database is more complicated. There is a mode in which SQLAlchemy determines the structure of your database directly from the database schema (instead of from model definition). Maybe that is what makes more sense in your case. Documentation link: http://docs.sqlalchemy.org/en/rel_0_8/core/reflection.html.

  • #357 Nick said 2015-02-18T19:02:31Z

    Thanks for your tutorial Miguel.

    After getting this far, I would recommend readers to use Flask-Migrate which you actually wrote :) It wraps Alembic which is the preferred migration tool for SQLAlchemy. It automatically detects changes and is very easy to use.

    https://github.com/miguelgrinberg/Flask-Migrate

    Simple code changes for this tutorial:

    === run.py === from app import manager manager.run()

    === app/init.py === from flask import Flask from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.script import Manager from flask.ext.migrate import Migrate, MigrateCommand

    app = Flask(name) app.config.from_object('config')

    db = SQLAlchemy(app) migrate = Migrate(app, db)

    manager = Manager(app) manager.add_command('db', MigrateCommand)

    Fixes circular import

    from app import views, models

    === Command Line === cd microblog ./run.py db init ./run.py db migrate -m "Added User and Post models" ./run.py db upgrade

    Good to go!

  • #358 Sua said 2015-02-21T21:15:28Z

    Hi Miguel,

    Thank you so much for putting this together and being so generous with your time. When I try to run ./db_create.py I get:

    File "db_create.py", line 3, in from config import SQLALCHEMY_DATABASE_URI ImportError: cannot import name SQLALCHEMY_DATABASE_URI

    I downgraded to sqlalchemy to version 0.7.9 like you suggested above and I'm still getting the same error.

  • #359 Miguel Grinberg said 2015-02-22T00:03:05Z

    @Sua: check your config.py file. You need to have a SQLALCHEMY_DATABASE_URI variable there with the database information. You are probably missing that, or you have a typo in the variable name.

  • #360 Colm said 2015-02-25T04:01:44Z

    Hi Miguel,

    I too will start my comment with a big thank you. Really encouraging to get going with your mega-tutorial.

    I wonder if you would have tips for using MongoDB instead of SQL and SQLAlchemy? I feel like mongo is much more legible for starters and much easier to manipulate. Also, before starting the megatutorial, I watched your O'Reilly webcast on !yt in which you used Mongo for your user demo.

    I am a beginner in Python and Flask, but I'm looking forwards to learning!

    Thanks again,

    all the best.

  • #361 Miguel Grinberg said 2015-02-25T06:48:45Z

    @Colm: You can probably find lots of MongDB tutorials for Python, and these are all applicable to Flask. I don't write much about MongoDB or other NoSQL databases because I'm much more familiar with relational databases.

  • #362 Josh said 2015-02-28T11:12:12Z

    HI Miguel, Absolutely fantastic tutorial series, I really cant thank you enough, it is without doubt one of the best I've ever read/done. I get something different after querying the users:

    users = models.User.query.all()

    users [, ] for u in users: ... print(u.id,u.nickname) ... (1, u'john') (2, u'susan')

    ie the output is in parenthesis and the user is prefixed by u and in quotes, unlike in your example. Is this something I should worry about? I dont really understant why it doesnt happen for your code and does for mine, any suggestions would be much appreciated, Josh

  • #363 Miguel Grinberg said 2015-03-01T01:08:38Z

    @Josh: the differences are related to differences between Python 2 and Python 3 and can be ignored.

  • #364 Josh said 2015-03-01T18:44:10Z

    Hi Miguel, thanks for the reply, I suspected it was due to something like that as i really couldn’t work out why it was happening, but its good to know for sure. And a little comment, which feels a bit rude given that you’ve put so much great stuff up here and given it all away for free! But as I sometimes read your blog on my phone, although the browser (chrome 40.0.2214.89 on android 4.12) displays most of it very well, the code boxes have a very small font. This means that as I'm often referring back to the code as I read the article I end up doing allot of pinching to zoom in on the code then back out on the explanation, which gets a little annoying. But if it comes down to it I, and I reckon most of your readers, would prefer you to spend time on writing new content than fixing incredibly minor details such as this. Just thought I should let you know though. Thanks again, for the reply and most of all the brilliant tutorial. Josh

  • #365 Josh said 2015-03-01T18:57:55Z

    Please disregard my previous comment about font size in the code boxes! I was wrong and was getting mixed up with something else I’ve been reading at the same time, I've just checked and your code boxes display perfectly on my phone. Many apologies for suggesting otherwise. The thanks still stand of course. For reference the blog on which they don't is: http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/

  • #366 Rich C said 2015-03-04T12:05:33Z

    About 3 years too late, but here we go anyway: C:\Python34\microblog>flask\Scripts\python db_create.py Traceback (most recent call last): File "db_create.py", line 1, in from migrate.versioning import api File "C:\Python34\microblog\flask\lib\site-packages\migrate\versioning\api.py", line 56 except exceptions.PathFoundError,e:

    SyntaxError: invalid syntax

    function in api.py:

    def create(repository,name,opts): try: rep=Repository.create(repository,name,opts) except exceptions.PathFoundError,e: raise exceptions.KnownError("The path %s already exists"%e.args[0])

  • #367 Miguel Grinberg said 2015-03-04T18:24:59Z

    @Rich: you get a failure because I think you already execute db_create before. The "db_repository" folder probably exists already, this script is trying to create it again.

  • #368 Volodimir said 2015-03-05T17:21:57Z

    Hi!

    When I write "from app import db, models" I have "ImportError: no module named app

    what am I doing wrong?

  • #369 Miguel Grinberg said 2015-03-06T19:54:36Z

    @Volodimir: the "app" folder should be in your current directory. See the code on github to compare against your version if you need to have a working reference.

  • #370 Mark said 2015-03-07T04:43:17Z

    This is incredible, and only the fourth part of this tutorial.. How in-depth you get into sqlalchemy and all these other things just to teach flask, I can't believe how much I am learning. It is so impressive, thank you for all the time you put into this. I may be new to this stuff, but thanks to you I already have a little flask site running

  • #371 dyChoi said 2015-03-08T16:03:07Z

    i'm korean. i use pyCharm.

    i have a problem.

    i enter, from migrate.versioning import api ... ...

    i got an error. this is, ImportError: No module named migrate.versioning

    i have a folder. Python27\Lib\site-packages\sqlalchemy_migrate.egg-info

    why got a problem?? :(

  • #372 Miguel Grinberg said 2015-03-09T23:15:12Z

    @dyChoi: Does it work when you test it outside of PyCharm?

  • #373 Wayne Gagin said 2015-03-11T21:53:31Z

    Hi Miguel, I am extremely new to programming and even using Linux as an OS having only installed Linux 2 weeks ago i am still learning the basic's of getting around the Konsole so i appreciate that i'm clearly trying to run before i can walk but thats always been my problem :). As ive always wanted to learn programming i found a Tutorial online "A Byte of Python" on completetion of that tutorial led me to here to learn to create websites and so far so good i find your explanations clear and straight forward enough for me to proceed with the Tutorial, i'm not fully understanding everything as yet but i am really enjoying learning and working my through your lessons, along the way i have had little issues which i've managed to overcome but the list associated with my current problem has stopped me in my tracks as i could well mess everything up attempting to solve this myself, I am on section 4 at Play time where i get the following error's when trying to create a user into the database and would therefore appreciate any help and advice on solving this issue in order for me to continue with your Tutorial

    from app import db, models u = models.User(nickname='john', email='john@email.com') Traceback (most recent call last): File "", line 1, in File "", line 2, in init File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py", line 324, in new_state_if_none state = self._state_constructor(instance, self) File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 725, in get obj.dict[self.name] = result = self.fget(obj) File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py", line 158, in _state_constructor self.dispatch.first_init(self, self.class) File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/event/attr.py", line 260, in call fn(args, *kw) File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2693, in _event_on_first_init configure_mappers() File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2589, in configure_mappers mapper._post_configure_properties() File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1694, in _post_configure_properties prop.init() File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/interfaces.py", line 144, in init self.do_init() File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1549, in do_init self._process_dependent_arguments() File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1605, in _process_dependent_arguments self.target = self.mapper.mapped_table File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 725, in get obj.dict[self.name] = result = self.fget(obj) File "/home/gilly/microblog/flask/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1535, in mapper % (self.key, type(argument))) sqlalchemy.exc.ArgumentError: relationship 'posts' expects a class or a mapper argument (received: )

    I hope to hear from you soon as i am eager to continue. Many Thanks, Wayne Gagin.

  • #374 Miguel Grinberg said 2015-03-12T05:39:41Z

    @Wayne: you need to compare your code against mine on Github, I bet there is something different regarding the posts relationship in the User model.

  • #375 Brian said 2015-03-14T01:24:38Z

    I've been able to follow the post instructions until running the python command line... When I enter "from app import db, models" I get:

    File "c:\blog_tutorial\app__init__.py", line 1, in from flask import Flask ImportError: cannot import name 'Flask'

    I'm not sure what I can try to fix this. --Brian