Select, Insert, Delete
**********************

Now that you have declared models it's time to query the data from the
database.  We will be using the model definitions from the Quickstart
chapter.


Inserting Records
=================

Before we can query something we will have to insert some data.  All
your models should have a constructor, so make sure to add one if you
forgot. Constructors are only used by you, not by SQLAlchemy
internally so it's entirely up to you how you define them.

Inserting data into the database is a three step process:

1. Create the Python object

2. Add it to the session

3. Commit the session

The session here is not the Flask session, but the Flask-SQLAlchemy
one. It is essentially a beefed up version of a database transaction.
This is how it works:

>>> from yourapp import User
>>> me = User('admin', 'admin@example.com')
>>> db.session.add(me)
>>> db.session.commit()

Alright, that was not hard.  What happens at what point?  Before you
add the object to the session, SQLAlchemy basically does not plan on
adding it to the transaction.  That is good because you can still
discard the changes.  For example think about creating the post at a
page but you only want to pass the post to the template for preview
rendering instead of storing it in the database.

The "add()" function call then adds the object.  It will issue an
*INSERT* statement for the database but because the transaction is
still not committed you won't get an ID back immediately.  If you do
the commit, your user will have an ID:

>>> me.id
1


Deleting Records
================

Deleting records is very similar, instead of "add()" use "delete()":

>>> db.session.delete(me)
>>> db.session.commit()


Querying Records
================

So how do we get data back out of our database?  For this purpose
Flask-SQLAlchemy provides a "query" attribute on your "Model" class.
When you access it you will get back a new query object over all
records.  You can then use methods like "filter()" to filter the
records before you fire the select with "all()" or "first()".  If you
want to go by primary key you can also use "get()".

The following queries assume following entries in the database:

+-------------+-------------+-----------------------+
| *id*        | *username*  | *email*               |
+-------------+-------------+-----------------------+
| 1           | admin       | admin@example.com     |
+-------------+-------------+-----------------------+
| 2           | peter       | peter@example.org     |
+-------------+-------------+-----------------------+
| 3           | guest       | guest@example.com     |
+-------------+-------------+-----------------------+

Retrieve a user by username:

>>> peter = User.query.filter_by(username='peter').first()
>>> peter.id
2
>>> peter.email
u'peter@example.org'

Same as above but for a non existing username gives *None*:

>>> missing = User.query.filter_by(username='missing').first()
>>> missing is None
True

Selecting a bunch of users by a more complex expression:

>>> User.query.filter(User.email.endswith('@example.com')).all()
[<User u'admin'>, <User u'guest'>]

Ordering users by something:

>>> User.query.order_by(User.username).all()
[<User u'admin'>, <User u'guest'>, <User u'peter'>]

Limiting users:

>>> User.query.limit(1).all()
[<User u'admin'>]

Getting user by primary key:

>>> User.query.get(1)
<User u'admin'>


Queries in Views
================

If you write a Flask view function it's often very handy to return a
404 error for missing entries.  Because this is a very common idiom,
Flask-SQLAlchemy provides a helper for this exact purpose.  Instead of
"get()" one can use "get_or_404()" and instead of "first()"
"first_or_404()". This will raise 404 errors instead of returning
*None*:

   @app.route('/user/<username>')
   def show_user(username):
       user = User.query.filter_by(username=username).first_or_404()
       return render_template('show_user.html', user=user)

Also, if you want to add a description with abort(), you can use it as
argument as well.

>>> User.query.filter_by(username=username).first_or_404(description='There is no data with {}'.format(username))
