Getting Started
After cloning and entering the repo, take the following steps to get started.
Environment
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew install postgresql sudo easy_install pip pip install virtualenv virtualenv env . env/bin/activate pip install -r requirements.txt
Database
pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start createdb apollo python manage.py db upgrade // if postgres role does not exist: sudo -u {{psql_user}} psql techla CREATE USER postgres SUPERUSER; \q
Populate the Database
python manage.py populate
Run the Server
python run.py
Application Structure
- app
- controllers
- models
- static
- templates
- __init__.py
- populate.py
- env
- migrations
- manage.py
- config.py
- run.py
app
The app folder contains all of the application code. app/__init__.py is run when the app module is imported, and it defines the create_app() function, which follows the Factory method design pattern. This Factory method configures the api routes and template routes that the server will support, by hooking the app object up to code written in the contollers and templates folders.
contollers
The controllers folder is where we set up "blueprints". In Flask, blueprints help us write portable api code, and in this case we only have one blueprint, use to define the "/question" routes. We define all of the "/question" routes in this file, and connect the blueprint to the app object in app/__init__.py.
The route functions defined in the "/question" routes have access to a request
object supplied by Flask. These functions extract data from the request and interact with the database based on whether or not the route is intended to insert, delete, update or query for database objects.
models
The models folder is where we define models to be used by the SQLAlchemy ORM (Object Relational Mapping). You can think of the orm as a wrapper around the database that abstracts away a bit of the relational logic and allows us to think about object. Each .py file in the models folder specficies the mapping for a different object that we will have a table for in the database. Most (probably all) objects that we have a table for in the database will need a .py file in the models folder. The objects we currently support are:
- Question objects
- Post objects
- Comment objects
- Vote Objects.
Question object have associated posts (yes, no maybe posts), Post objects have associated Comment objects, Comment objects have associated Vote objects.
templates
The templates folder contains Jinja templates. These are basically html files that have additional syntax allowing for more complex logic. We can pass data into a Jinja template, and render the template as an html file with this data included however we like. For example, we can pass a list of Question objects into a jinja template and render the list of questions.
static
This folder is where we store assets like css & js files and images. We static files in our templates using the url_for() method.
populate.py
This is a startup script that initializes the database with some dummy data for testing & prototyping.
env
This folder is generated by the command virtualenv env
. Whenever we install dependencies (like when we run pip install -r requirements.txt
), the will be installed to this folder. This is desireable, as it allows us to ensure that the development environments are consistant across the team. Any time you enter the apollo directory, you need to run . env/bin/activate
to "enter" the virtual environment.
migrations
This folder stores all the migrations data for the database. Forunately, we do not need to write migrations manually. The manage.py files gives us access to a few commands that set up the postgresql database from the SQLAlchemy models defined in models.py. When first setting up the development environment, we run python manage.py db init
, which creates the migrations folder. After that, whenever we update or add models to our app, we run python manage.py db migrate
, which creates the migration file that contains the information about how to correctly update the database, and python manage.py db upgrade
, which actually applies the migration.
config.py
This file defines config parameters for Testing, Development, and Production environments.
manage.py
This file defines some useful commands:
- python manage.py db init (sets up the migrations folder)
- python manage.py db migrate (generates a migration file that describes changes in DB from previous migration)
- python manage.py db upgrade (actually migrates the DB from current state to latest migration)
- python manage.py populate (populates db with dummy data)
run.py
This file uses create_app to get an instance of an app object and starts the server. To start the server, run python run.py
. Make sure you have the virtualenv activated!
Working with the ORM
To query object from the database, you will need to import the class from the models file. Example:
from app.models import Post all_posts = Post.query.all()
The all() and first() methods of the query object will return an array of all objects meeting the query and the first object meeting the query respectively. If there are no rows in the db matching the query, the functions will return the None type.
To add on object to the database, you will need to import the db
object from the models directory as well:
from app.models import db, Comment new_comment_args = { "contents": "I love this post!", "post_id": 28 # This is the id of the post the comment is connected to, } comment = Comment(**new_comment_args) db.session.add(comment) db.session.commit()
Another example, querying with a filter and updating an object:
from app.models import db, Post post = Post.query.filter(Post.answer == "yes").first() post.author_first = "Awaycoolerfirstname" db.session.add(post) db.session.commit()
Working with templates
Checkout the index route that is setup in app/__init__.py to see how to set up a template route and how to pass data from the db into the templates. Under the hood, Flask generates the html files on the fly using the data passed into the template with the render_template function provided by Flask.