Skip to content

Instantly share code, notes, and snippets.

@joequery
Created July 9, 2012 17:43
Show Gist options
  • Save joequery/3077833 to your computer and use it in GitHub Desktop.
Save joequery/3077833 to your computer and use it in GitHub Desktop.

Revisions

  1. joequery created this gist Jul 9, 2012.
    424 changes: 424 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,424 @@
    [Flask](http://flask.pocoo.org/) is a Python web framework that's steadily
    increasing in popularity within the webdev world. After reading so many
    great things about Flask, I decided to try it out myself. I personally find
    testing out a new framework difficult, because you must find a project complex
    enough to reveal the framework's quirks, but not so daunting as to take the
    fun out of the project. Luckily, my PHP/Wordpress powered website filled this
    role quite nicely - the website simply consists of static content, a contact
    page, and a blog. If I could not convert such a simple site over to Flask,
    I would immediately know that Flask and I would not make a good team.

    Spoiler alert: You're reading this on a Flask powered site! Feel free to
    check out the source of this site
    [on Github](https://github.com/joequery/Vert-Flask).

    Getting Started
    ---------------

    The first task at hand was simply getting the home, about, services, and work
    pages to render correctly. While the task consisted mostly of copying and
    pasting, I was able to immediately apply most of the concepts from the
    [Flask quickstart guide][0]. Specifically, I had to learn


    * How do I actually start the server?
    * Where do my templates go?
    * Where do my static assets go?
    * How do I retrieve my assets from within the templates?
    * How do I route requests?

    Sure enough, getting the pure HTML documents to render was pretty simple.
    By pure HTML, I mean there were no includes for headers, footers, etc. Thus
    the logical next step was to take these pure HTML documents and DRY up the
    repeating areas.

    Growing pains...already?!
    ------------------------

    Coming from PHP, I was used to just `include`-ing the content I wanted and being
    done with it. [Jinja2](http://jinja.pocoo.org/docs/), Flask's default
    templating engine, does not currently support includes. (Note: An `include`
    function [does exist](http://jinja.pocoo.org/docs/templates/#include). However,
    I could not get the include function to return non-escaped html. Perhaps this
    function was broken in a recent build). Jinja does, however, have
    [template inheritance][1]. To demonstrate, suppose we have a parent
    template `layout.html`

    {% raw %}
    <pre class="prettyprint">&lt;!DOCTYPE html&gt;
    &lt;html lang="en"&gt;
    &lt;head&gt;&lt;title&gt;My Website&lt;/title&gt; &lt;/head&gt;
    &lt;body&gt;
    &lt;div id="wrapper"&gt;
    &lt;div id="content"&gt;
    {% block body %}{% endblock body %}
    &lt;/div&gt;&lt;!--content--&gt;
    &lt;/div&gt;&lt;!--wrapper--&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    </pre>
    {% endraw %}

    And a child template `index.html` that inherits the parent and fills in its
    blocks
    {% raw %}
    <pre class="prettyprint">{% extends "layout.html" %}
    {% block body %}
    Body content goes here!
    {% endblock body %}
    </pre>
    {% endraw %}

    If we were to call [render_template][2] on `index.html`, this is what we
    would get.

    <pre class="prettyprint">
    &lt;!DOCTYPE html&gt;
    &lt;html lang="en"&gt;
    &lt;head&gt;&lt;title&gt;My Website&lt;/title&gt; &lt;/head&gt;
    &lt;body&gt;
    &lt;div id="wrapper"&gt;
    &lt;div id="content"&gt;
    Body content goes here!
    &lt;/div&gt;&lt;!--content--&gt;
    &lt;/div&gt;&lt;!--wrapper--&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    </pre>

    I like the template inheritance pattern of Jinja, but the lack of an include
    statement can easily lead to huge parent templates.

    Thankfully, I did find a sufficient workaround to the lack of an include
    statement. Jinja lets you define [Macros][4], which are essentially functions
    used within templates to render something. I defined a macro called `nav` that
    simply returned the HTML for the navigation. I did the same thing for `header`,
    `footer`, and any other sections repeated throughout the site. Take our
    footer, for example.

    {% raw %}
    <pre class="prettyprint">
    {% macro footer() %}
    &lt;div id="footer"&gt;
    &lt;div class="hr"&gt;&lt;hr /&gt;&lt;/div&gt;
    &lt;p&gt;
    &lt;a href="mailto:[email protected]"&gt;[email protected]&lt;/a&gt; |
    &lt;a href="http://www.twitter.com/vertstudios"&gt;@vertstudios&lt;/a&gt;
    &lt;/p&gt;
    &lt;/div&gt;
    {% endmacro %}
    </pre>
    {% endraw %}

    Then, from `layouts.html`, I could call the footer macro. Flask requires the
    use of the `|safe` filter if you want to render the returned string. Thus, a
    macro `foo()` can be called from a macro `bar()` without the need to specify
    `{{'{{'}}foo()|safe{{'}}'}}`. But since `layouts.html` is something being
    directly rendered, we need to specify `|safe` every time we call a macro.

    So the final `layouts.html` ended up looking like

    {% raw %}
    <pre class="prettyprint">
    {% from "sections" import meta_and_css, nav, javascripts, footer %}
    &lt;!DOCTYPE html&gt;
    &lt;html lang="en"&gt;
    {{ meta_and_css(g, title)|safe }}
    &lt;body onload="prettyPrint()" id="{{g.bodyID}}"&gt;
    &lt;div id="wrapper"&gt;
    {{ nav(g)|safe }}
    &lt;div id="content"&gt;
    {% block body %}{% endblock body %}
    &lt;/div&gt;&lt;!--content--&gt;
    {{ footer()|safe }}
    &lt;/div&gt;&lt;!--wrapper--&gt;
    {{ javascripts(g)|safe }}
    &lt;/body&gt;
    &lt;/html&gt;
    </pre>
    {% endraw %}

    This was less than ideal, but it worked; a recurring theme throughout the
    development process that surprisingly didn't bother me so much.

    Being 'Brave'
    -------------

    Flask didn't always have an out-of-the-box solution to the problem at hand. In
    fact, it *rarely* did. Flask did, however, give me all the tools necessary to
    create whatever I wanted. In a way, I felt that Flask itself was telling me
    "Joseph, you're smart enough to implement X functionality on your own... be
    brave for once!"

    Sometimes reinventing the wheel is a rewarding exercise.

    The Contact Form
    ----------------

    The recommended method of dealing with forms in Flask is the [Flask-WTF][5]
    extension. Flask-WTF has many [built in validation methods][6], but it did not
    include validation for a phone number. No big deal, any form library worth its
    salt has means for defining custom validation functions. WTForms does in fact
    have a mechanism for implementing [custom validators][7], but I did not find
    their examples very pleasing to the eye:

    <pre class="prettyprint">
    def length(min=-1, max=-1):
    message = 'Must be between %d and %d characters long.' % (min, max)

    def _length(form, field):
    l = field.data and len(field.data) or 0
    if l < min or max != -1 and l > max:
    raise ValidationError(message)

    return _length

    class MyForm(Form):
    name = TextField('Name', [Required(), length(max=50)])
    </pre>

    Consequently, I used Flask-WTF to gather data from the form fields, but I
    rolled my own validation module, which only took about an hour.

    The actual sending of the email was easily handled with Python's built-in
    `smtplib` module and Gmail.

    The Blog
    --------

    Creating a blog has become the "Hello, World" of web application frameworks.
    While [Python blogging software][8] does exist, it seemed like it would take
    longer to integrate an existing blogging platform into my Flask project than
    just rolling my own.

    For the past year or so, I've longed for a file-system based blogging platform.
    Writing my articles in VIM + Markdown is **so** much more enjoyable than
    writing HTML in VIM, moving the HTML over to Wordpress, previewing the HTML,
    making sure the formatting is right, and other annoying steps.

    There were a few specific things I needed to accomplish with the blog:

    * Ability to generate an RSS feed
    * The URLs of the blog posts must be the same as before.
    * Pagination

    Without the need to generate an RSS feed, the only thing I would need to do is
    route `/blog/<post>/` to a function that attempts to render a template
    named `/blog/the-post-url`, and return a 404 if that template isn't found.
    The RSS requirement, however, forced me to think how I should go about storing
    the meta data for each post.

    I settled on a fairly simple structure that, while not beautiful or clever,
    works good enough for me. I create a directory in `/blog/posts/` named `desired
    -post-url`. Within that directory, I create two files: `meta.py` and `body.html
    `. `meta.py` just contains a few variables for metadata (date posted, excerpt,
    etc), and body.html has the post body.

    To avoid having to parse through all the metadata files to figure out the
    order of the posts for the RSS feed, I just prepend the directory name to the
    top of a file called `rss.txt`. Again, not the most beautiful thing in the
    world but it's a system that I find easy to understand and easy to work with.

    The RSS feed and blog pagination are handled in the same step. I run a script
    called `genfeed.py` which creates the rss xml file and pagination files.
    Using static files generated by the `geenfeed.py` script (which reads the
    `rss.txt` script mentioned earlier) for pagination makes sense because the
    pagination should only change whenever I update/create a story.

    ### Importing my Blog Posts

    To import my Wordpress posts into this new structure, all I had to do was
    parse Wordpress's export xml file with [PyQuery][9] and create the files with
    standard library functions. I didn't bother to convert the posts from Wordpress
    to Markdown, so all old posts you look through in the source will have normal
    HTML.


    Structural Troubles
    -------------------

    One of the biggest troubles I faced throughout the site development was how
    to import things into particular files. This [StackOverflow answer][10]
    describes the steps necessary to import a module in a parent/sibling directory
    when running a script directly. Consequently, any scripts that needed access to
    the `app` object (which holds a lot of important methods and attributes about
    the application instance), I had to either run
    `python -m mywebsite.blog.genfeed`, or just move `genfeed.py` to the same
    directory as `mywebsite.py`, which would enable me to run `python genfeed.py`.
    I chose the latter out of sheer laziness, though I'm not particularly happy
    I had to make such a choice. I should note that this is not a Flask specific
    issue, but a Python issue.

    Development vs Production Environment
    -------------------------------------

    In development, all assets are served from the static files. In production,
    all assets are served from S3. The environment is set by a env-var called
    `FLASK_ENV`. I needed a way to let my templates know "Hey, you're in the
    production environment, you should link to S3 files instead of static files!".
    I decided to take advantage of Flask's `g` object, which is a global object
    that exists througout the request context. You're free to populate it with
    whatever you want. I created and used the property `g.assets`.

    While this suited my needs perfectly, it caused my templates to appear
    slightly less pretty. Anytime I wanted to call a macro that looks for the
    `g.assets` property, I'd have to pass `g` to it. For example,
    here's the definition of the `img` macro, a function used to render images
    on the site:

    {% raw %}
    <pre class="prettyprint">
    {% macro img(g, file, alt="", class="") %}
    &lt;img
    src="{{g.assets}}/images/{{file}}"
    alt="{{alt}}"
    class="{{class}}"
    /&gt;
    {% endmacro %}
    </pre>
    {% endraw %}

    An example call to the macro:
    {% raw %}
    <pre class="prettyprint">
    {{img(g, "pointer.png", "", "pointer")|safe }}
    </pre>
    {% endraw %}

    It's more verbose than what I'd really like, but it gets the job done.

    Going Live with Nginx and uWSGI
    -------------------------------

    Now that my project was pretty much finished, it was time to put the files
    on my Linode VPS and show my project to the world! I decided to use Nginx with
    uWSGI, simply because the pair have been the subject of many tutorials. Flask
    itself [has documentation][11] on uWSGI. However, I found parts of the
    documentation inadequate.

    From the documentation:

    <pre class="prettyprint">
    # Given a flask application in myapp.py, use the following command:
    $ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
    </pre>

    If you want to put a project on a live server, there's no way you should have
    to do so much work! uWSGI has [ini configuration files][12] that I took full
    advantage of. Now, instead of executing a verbose command from the shell, I can
    simply execute

    <pre class="prettyprint">
    $ uwsgi uwsgi.ini
    </pre>

    My `uwsgi.ini` is as follows:

    <pre class="prettyprint">
    [uwsgi]
    project = vertstudios
    daemonize = /var/log/nginx/access.log
    master = true
    chdir = /var/www/vertstudios.com
    socket = 127.0.0.1:5000
    wsgi = %(project):variable holding app instance
    virtualenv = env/
    pidfile = /var/run/uwsgi/%(project).pid
    touch-reload = /var/run/uwsgi/%(project).pid
    processes = 3
    procname-prefix = %(project)
    </pre>

    Now my uWSGI configuration is under version control with the rest of my project.

    Here's an explanation for the parameters:

    * project: The module name where you call `app.run()`.
    * daemonize: If you want to daemonize. For some reason, you have to specify a
    log file path or uWSGI will just shove things down `stdin`. `true`
    did not work for me.
    * master: Boolean indicating if you want a master process. Master processes
    make [uwsgi management][13] *much* easier.
    * chdir: Change directory. Essentially lets you keep parameters such as
    "project" and virtualenv relative to the path provided to chdir.
    * socket: A Unix socket path or TCP socket info
    * wsgi: %(the module containing app):app
    * virtualenv: Path to the virtualenv for the project.
    * pidfile: Where the master process pid file will be written.
    * touch-reload: Touching this file causes the processes to reload (convenient!)
    * processes: How many processes you want running (not including master process)
    * procname-prefix: Process name prefix. Useful for `ps aux | grep myapp`

    Now, for the parts of my nginx config relevant to uWSGI:

    <pre class="prettyprint">
    # Redirect everything to the main site.
    server {
    server_name www.vertstudios.com;
    return 301 http://vertstudios.com$request_uri;
    }

    server {
    server_name vertstudios.com;
    location / { try_files $uri @flaskapp; }
    location @flaskapp {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:5000;
    }
    }
    </pre>

    Things left TODO
    ----------------

    I felt I had done enough to merit scrapping my PHP/Wordpress site and put the
    Flask site up in its place. Despite that, there's still quite a bit of work to
    be done.

    * I plan on exporting my Wordpress comments to Disqus. I enjoy interacting with
    people that read my articles, and I'd like to preserve the comments I've
    already acquired.

    * I've managed to break a few links. In particular, I've broken links to php
    files I stupidly used to demo some of my jQuery plugins.

    * Using [prettify][14] for syntax highlighting means I have to still use &lt;pre&gt;
    tags in my documents, since the script requires a class of "prettyprint" on
    those tags. I'll either adjust prettyprint js and styles myself, find an
    alternative syntax highlighter, or do some processing of the markup to append
    the "prettyprint" class whenever it finds the &lt;pre&gt; tag.

    * Still need to set up git deployment. I'm currently just pulling from a
    private bitbucket repo on my server.

    * I need to automate my build workflow. A single bash script should concat all
    my CSS/JS files, run YUICompressor, and upload the files to S3.

    * I really need to implement meta description tags for my posts...but that
    would require me to actually write them.

    Overall Impressions
    -------------------

    As a whole, I'm quite pleased with Flask. For the most part, it gets out of
    your way and lets you build things how you want to build them. Consequently,
    your project structure may be sloppy (especially when first learning Flask),
    but you're guaranteed to understand how and why everything works.



    [0]: http://flask.pocoo.org/docs/quickstart
    [1]: http://jinja.pocoo.org/docs/templates/#template-inheritance
    [2]: http://flask.pocoo.org/docs/quickstart/#rendering-templates
    [3]: http://flask.pocoo.org/community/poweredby/
    [4]: http://jinja.pocoo.org/docs/templates/#macros
    [5]: http://packages.python.org/Flask-WTF/
    [6]: http://flask.pocoo.org/docs/patterns/wtforms/#the-forms
    [7]: http://wtforms.simplecodes.com/docs/1.0.1/validators.html
    [8]: http://wiki.python.org/moin/PythonBlogSoftware
    [9]: http://packages.python.org/pyquery/api.html
    [10]: http://stackoverflow.com/a/9123510/670823
    [11]: http://flask.pocoo.org/docs/deploying/uwsgi/
    [12]: http://projects.unbit.it/uwsgi/wiki/INIFiles
    [13]: http://projects.unbit.it/uwsgi/wiki/Management
    [14]: http://google-code-prettify.googlecode.com/svn/trunk/README.html