GAEO Documentation

Make Donations

Recent site activity

Tutorials‎ > ‎

Create a Simple Blog by Using GAEO


In this page, we will show you how to use GAEO to create your own blog. 
NOTE: Your GAEO's version MUST be at least revision #68
  1. Create a blog project.

    In your work directory, use gaeo.py to create a blog project.


    and then, the utility created an blog directory that contains the basic files.

  2. Create models

    To store the blog posts and comments in the AppEngine's datastore, we have define the post and comment model. You can create model by using gaeogen.py utility.
    The difference between model and scaffold is that scaffold creates not only the model but also the controller and the template.

    After created the models, now, you'll see the model's fields.

     application/model/post.py
    from google.appengine.ext import db
    from gaeo.model import BaseModel

    class Post(BaseModel):
        content = db.TextProperty(required=True)
        post_at = db.DateTimeProperty(required=True, auto_now_add=True)
        title = db.StringProperty(required=True)

    Post model is used to store the articles in the blog. We keep titlecontent, and post_at fields in this model.

     application/model/comment.py
    from google.appengine.ext import db
    from gaeo.model import BaseModel

    from model.post import Post

    class Comment(BaseModel):
        author = db.StringProperty(required=True)
        content = db.TextProperty(required=True)
        posted_at = db.DateTimeProperty(required=True, auto_now_add=True)
        
    Comment.belongs_to(Post)

    Since there's a one-to-many relation between Post and Comment, we invoke a BaseModel class method, belongs_to, to construct the relation. After calling this method, a comment entity can access its post by using post field and a post entity can use comments field to get its comments.

  3. Create a post controller

    Now, we have to create pages and a controller to write and save articles. 

    The scaffold helped us create a post controller and four templates, new, show, index and create. The new template is used to write an article, edit the new template as the following:

     application/templates/post/new.html
    <form method="post" action="/post/create">
      <fieldset>
        <legend>Post a new article</legend>
        <p>
          <label for="title">Title: </label><br/>
          <input type="text" name="title" id="title" size="20" tabindex="1" />
        </p>
        <p>
          <label for="content">Content: </label><br/>
          <textarea id="content" name="content" rows="5" cols="40"></textarea>
        </p>
        <p>
          <input type="submit" value="Post"/>
        </p>
      </fieldset>
    </form>

    You can use http://localhost:8080/post/new to see the page.


    However you cannot post any article now because we haven't implemented the /post/create action yet. Now, let's define a create method in the post controller.

    application/controller/post.py
    from gaeo.controller import BaseController

    from model.post import Post

    class PostController(BaseController):
        def new(self):
            pass

        def show(self):
            pass

        def create(self):
            Post(title=self.params['title'],
                 content=self.params['content']).put()
            self.redirect('/')

    Once you pushing the 'Post' button, the form sends data to the /post/create action and the data are stored in the self.params dictionary. After storing the post (by invoking put method), we call self.redirect('/') to redirect the page to the root.

  4. List all articles

    Now, the post articles are stored in the datastore and we want to list all posts in the root (/). Let's modify the welcome controller.

    application/controller/welcome.py
    from gaeo.controller import BaseController
    from model.post import Post

    class WelcomeController(BaseController):
        def index(self):
            p = Post.all()
            p.order('-post_at')
            self.posts = p.fetch(10)

    We use the Datastore API, fetch(), to fetch 10 posts from datastore and store them in a member variable, self.posts. Then, use order() to sort the posts in descending order (by post_at field). Besides, we have also modify the welcome/index template to show the posts' titles.

    application/templates/welcome/index.html
    {% if posts %}
        {% for post in posts %}
        <div>{{ post.post_at }}</div>
        <h1><a href="/post/show/{{ post.key }}">{{ post.title }}</a></h1>
        <hr/>
        {% endfor %}
    {% else %}
        <h1>No Posts</h1>
    {% endif %}

    <a href="/post/new">Post an new article</a>

    Since we store the posts in the self.posts, GAEO helps us to send it to the template rendering engine. Therefore, you can directly use ths posts variable in the welcome/index template. We list each post's title and create a link to show the content. The index page should be like this:


    We will describe how to show the article in the next step.

  5. Show An Article

    In step 4, we use a link /post/show/<key> to show a single article. This URL makes GAEO invokes show action in PostController  with an id parameter (see your main.py/:controller/:action/:id is one of default routings). You can modify the show method in PostController to fetch the specified article.

    application/controller/post.py
    from model.post import Post
    ...
        def show(self):
            self.post = Post.get(self.params['id'])
    ...

    The model's get method will fetch data according to a key. Since we send the key through the id parameter, we fetch the specified post by filling the parameter to the get method. As above, we store the post data in the self.post member variable.

    We have to edit the post/show template to display the article.

    application/templates/post/show.html
    {% if post %}
      <h1 class="title">{{ post.title }}</h1>
      <hr/>
      <div class="content">
        {{ post.content }}
      </div>
      <hr/>
      <div class="meta">
        Post at {{ post.post_at|date }}
      </div>
    {% else %}
      <h1>Article Not Found</h1>
    {% endif %}
    <a href="/">&laquo; Back</a>

    Then, when you click a link to an article, you will get its content.


    So far, you've already constructed a simple blog.

  6. Add Comments

    We have not add comment fields in each article yet. If you want to hear the visitors' voice, you should add a form for them to leaving their comments. It's not hard to add comments support. Let's modify the post/show template.

    application/templates/post/show.html
    {% if post %}
      <h1 class="title">{{ post.title }}</h1>
      <hr/>
      <div class="content">
        {{ post.content }}
      </div>
      <hr/>
      <div class="meta">
        Post at {{ post.post_at|date }}
      </div>
      <hr/>
      <form method="post" action="/comment/post">
        <input type="hidden" name="post_key" value="{{ post.key }}"/>
        <p><label>Name:</label> <input type="text" name="author"/></p>
        <p><label>Comment:</label><br/><textarea name="comment"></textarea></p>
        <p><input type="submit" value="Leave comment"/></p>
      </form>
      <hr/>
    {% else %}
      <h1>Article Not Found</h1>
    {% endif %}
    <a href="/">&laquo; Back</a>

    The highlighted block is the comment form. We use a hidden value to keep track of the key of the article and two input fields for visitors to describe her name and comment. When the visitor push the submit button (Leave comment), the form data will be sent CommentController, which we haven't created. As above, we use gaeogen.py utility to generate this controller:


    and add some codes in the comment.py

    application/controller/comment.py
    from gaeo.controller import BaseController

    from model.post import Post
    from model.comment import Comment

    class CommentController(BaseController):
        def post(self):
            p = Post.get(self.params['post_key'])
            c = Comment(post=p,
                        author=self.params['author'],
                        content=self.params['comment'])
            c.put()
            self.redirect('/post/show/%s' % self.params['post_key'])

    Since we have defined a belongs_to(Post) relation in the comment model, it will be added a reference property to the post model. We make the comment referenced to the post by setting the post attribute on the instance creation time. After storing the comment, go back to the post.

    However, the post/show template doesn't show the comments now, so we need to add few codes in the template:

    application/templates/post/show.html
    {% if post %}
      <h1 class="title">{{ post.title }}</h1>
      <hr/>
      <div class="content">
        {{ post.content }}
      </div>
      <hr/>
      <div class="meta">
        Post at {{ post.post_at|date }}
      </div>
      <hr/>
      <h3>Comments</h3>
      {% if post.comments %}
      <ul>
          {% for c in post.comments %}
          <li>{{ c.author }} say "{{ c.content }}"</li>
          {% endfor %}
      </ul>
      {% endif %}
      <hr/>
      <h3>Leave a comment</h3>
      <form method="post" action="/comment/post">
        <input type="hidden" name="post_key" value="{{ post.key }}"/>
        <p><label>Name:</label> <input type="text" name="author"/></p>
        <p><label>Comment:</label><br/><textarea name="comment"></textarea></p>
        <p><input type="submit" value="Leave comment"/></p>
      </form>
      <hr/>
    {% else %}
      <h1>Article Not Found</h1>
    {% endif %}
    <a href="/">&laquo; Back</a>

    The hightlighted block shows the article's comments. The post.comments field is created because we declare a reference in the comment model.

  7. Finish

    Finally, you have a very very simple blog system now.