Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Python on Google App Engine: Creating blog engine. Part 2


  • Please log in to reply
No replies to this topic

#1 Vladimir

Vladimir

    CC Resident

  • Just Joined
  • PipPipPipPip
  • 79 posts

Posted 28 November 2010 - 02:44 PM

In last part we configured very basic Flask application for Google App Engine. In this part we will add some models and forms to our application. Let's start with model Post which represents blog post. Create file blog/models.py:

from google.appengine.ext import db

class Post(db.Model):
    title = db.StringProperty(required=True)

    content = db.TextProperty(required=True)
    content_html = db.TextProperty(required=True)

    # we cache category_name within Post to avoid JOIN query
    category_name = db.CategoryProperty(required=True)
    category = db.ReferenceProperty(Category, required=True)

    # we also cache tags for same reason
    tags = db.StringListProperty(required=True)

    # these 2 fields will be autopopulated by GAE, see docs for more info
    created_at = db.DateTimeProperty(required=True, auto_now_add=True)
    updated_at = db.DateTimeProperty(required=True, auto_now=True)

The code is pretty straightforward. Now we have to create form that will represent our Post model. Flask has very nice integration with WTForms and we are going to use it, because WTForms officially supports standard App Engine models. That means that WTForms is able to generate form from supplied model. You have to download WTForms and Flask-WTF extension. To generate form from existing model we have to use method model_form(). Create file blog/forms.py:

from flaskext import wtf
from wtforms.ext.appengine.db import model_form

# model_form() will collect properties from models.Port and convert them to appropriate fields
PostForm = model_form(models.Post, base_class=wtf.Form)

Now we have model that can store our post and form that can display it. We can implement view that will create posts using model and form. Create file blog/views.py:

# this view is accessible via URL http://localhost:8080/create-post using GET and POST methods
@module.route('/create-post', methods=['GET', 'POST'])
def create_post():
    form = forms.PostForm()
    if form.validate_on_submit(): # form was submitted and validated
        flash(u'Post successfully create') # notify user
        # create post 
        post = models.Post(title=form.title.data, content=form.content.data) using data from form
        # save post in datastore
        post.put()
        # redirect user to the post
        return redirect(post.get_absolute_url())
    # if form was not submitted or user provided invalid data just render template with our form
    return render_template('blog/create_post.html', form=form)

The only thing that I still did not explained is rendering form. We are using Jinja2 as template engine and we will use some reusable functions (macros in Jinja2 terms) that will help us render our form. You can see these macros at github. Template that renders form is located at blog/post/_form.html:

<!-- Import form macro I mentioned above -->
{% from '_form_macros.html' import form_field_td %}

<form method="post" action="" enctype="multipart/form-data">
  <table>
    <!-- Here we manually render each field -->
    <!-- It is boring but with this approach we can easily move and group fields -->
    <tr>{{ form_field_td(form.title) }}</tr>
    <tr>{{ form_field_td(form.category_name) }}</tr>
    <tr>{{ form_field_td(form.content) }}</tr>
  </table>
  <fieldset class="submit"> 
    <input type="submit" name="submit" value="Submit"> 
  </fieldset>
</form>

The last thing that we will do today is displaying posts. To do this we have to create new view:

# this view will be accessible via URL like http://localhost/view/flask-on-gae
@module.route('/view/<slug>', methods=['GET'])
def view_post(slug):
    # slug is used as key for model
    # get post by key
    post = models.Post.get_by_key_name(slug)
    return render_template('blog/post/post.html', post=post)

Template that renders post is very simple:

{% extends 'base.html' %}

{% block body %}
<h1>{{ post.title }}</h1>

<p>Category: {{ post.category_name }} @ {{ post.created_at }}</p>

{{ post.content_html|safe }}
{% endblock body %}

I think it is enough for today. I don't explain a lot of things that are covered by official docs. If you don't understand anything please ask questions. Demo is here: Flask on GAE

Edited by Roger, 29 November 2010 - 10:19 AM.
added hyperlink

  • 0




Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download