User Profile

Creating a user profile page does not really require any new major
concepts to be introduced. We just need to create a new view function
and its accompanying HTML template.

Here is the view function (file app/views.py):

```
@app.route('/user/<nickname>')
@login_required
def user(nickname):
    user = User.query.filter_by(nickname = nickname).first()
    if user == None:
        flash('User ' + nickname + ' not found.')
        return redirect(url_for('index'))
    posts = [
        { 'author': user, 'body': 'Test post #1' },
        { 'author': user, 'body': 'Test post #2' }
    ]
    return render_template('user.html',
        user = user,
        posts = posts)
```
The @app.route decorator that we used to declare this view function
looks a little bit different than the previous ones. In this case we
have an argument in it, which is indicated as <nickname>. This
translates into an argument of the same name added to the view function.
When the client requests, say, URL /user/miguel the view function will
be invoked with nickname = 'miguel'.

The implementation of the view function should have no surprises. First
we try to load the user from the database, using the nickname that we
received as argument. If that doesn't work then we just redirect to the
main page with an error message, as we have seen in the previous
chapter.

Once we have our user, we just send it in the render_template call,
along with some fake posts. Note that in the user profile page we will
be displaying only posts by this user, so our fake posts have the author
field correctly set.

Our initial view template will be extremely simple (file
app/templates/user.html):

```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>User: {{user.nickname}}!</h1>
<hr>
{% for post in posts %}
<p>
  {{post.author.nickname}} says: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}
```
The profile page is now complete, but a link to it does not exist
anywhere in the web site. To make it a bit more easy for a user to check
his or her own profile, we are going to add a link to it in the
navigation bar at the top (file `app/templates/base.html'):
```
    <div>Microblog:
        <a href="{{ url_for('index') }}">Home</a>
        {% if g.user.is_authenticated() %}
        | <a href="{{ url_for('user', nickname = g.user.nickname)
}}">Your Profile</a>
        | <a href="{{ url_for('logout') }}">Logout</a>
        {% endif %}
    </div>
```
Note how in the url_for function we have added the required nickname
argument.

Give the application a try now. Clicking on the Your Profile link at the
top should take you to your user page. Since we don't have any links
that will direct you to an arbitrary user profile page you will need to
type the URL by hand if you want to see someone else. For example, you
would type http://localhost:5000/user/miguel to see the profile of user
miguel.
Avatar

I'm sure you agree that our profile pages are pretty boring. To make
them a bit more interesting, let's add user avatars.

Instead of having to deal with a possibly large collection of uploaded
images in our server, we will rely on the Gravatar service to provide
our user avatars.

Since returning an avatar is a user related task, we will be putting it
in the User class (file app/models.py):
```
from hashlib import md5
# ...
class User(db.Model):
    # ...
    def avatar(self, size):
        return 'http://www.gravatar.com/avatar/' +
md5(self.email).hexdigest() + '?d=mm&s=' + str(size)
```
The avatar method of User returns the URL of the user's avatar image,
scaled to the requested size in pixels.

Turns out with the Gravatar service this is really easy to do. You just
need to create an md5 hash of the user email and then incorporate it
into the specially crafted URL that you see above. After the md5 of the
email you can provide a number of options to customize the avatar. The
d=mm determines what placeholder image is returned when a user does not
have an Gravatar account. The mm option returns the "mystery man" image,
a gray silhouette of a person. The s=N option requests the avatar scaled
to the given size in pixels.

The Gravatar's website has documentation for the avatar URL.

Now that our User class knows how to return avatar images, we can
incorporate them into our profile page layout (file
app/templates/user.html):
```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td><h1>User: {{user.nickname}}</h1></td>
    </tr>
</table>
<hr>
{% for post in posts %}
<p>
  {{post.author.nickname}} says: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}
```
The nice thing about making the User class responsible for returning
avatars is that if some day we decide Gravatar avatars are not what we
want, we just rewrite the avatar method to return different URLs (even
ones that points to our own web server, if we decide we want to host our
own avatars), and all our templates will start showing the new avatars
automatically.

We have added the user avatar to the top of the profile page, but at the
bottom of the page we have posts, and those could have a little avatar
as well. For the user profile page we will of course be showing the same
avatar for all the posts, but then when we move this functionality to
the main page we will have each post decorated with the author's avatar,
and that will look really nice.

To show avatars for the posts we just need to make a small change in our
template (file `app/templates/user.html'):
```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td><h1>User: {{user.nickname}}</h1></td>
    </tr>
</table>
<hr>
{% for post in posts %}
<table>
    <tr valign="top">
        <td><img
src="{{post.author.avatar(50)}}"></td><td><i>{{post.author.nickname}}
says:</i><br>{{post.body}}</td>
    </tr>
</table>
{% endfor %}
{% endblock %}
```
Reusing at the sub-template level

We designed the user profile page so that it displays the posts written
by the user. Our index page also displays posts, this time of any user.
So now we have two templates that will need to display posts made by
users. We could just copy/paste the portion of the template that deals
with the rendering of a post, but that is really not ideal, because when
we decide to change the layout of a post we'll have to remember to go
update all the templates that have posts in them.

Instead, we are going to make a sub-template that just renders a post,
then we'll include it in all the templates that need it.

To start, we create a post sub-template, which is really nothing more
than a regular template. We do so by simply extracting the HTML for a
post from our user template (file /app/templates/post.html):
```
<table>
    <tr valign="top">
        <td><img
src="{{post.author.avatar(50)}}"></td><td><i>{{post.author.nickname}}
says:</i><br>{{post.body}}</td>
    </tr>
</table>
```
And then we invoke this sub-template from our user template using
Jinja2's include command (file app/templates/user.html):
```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td><h1>User: {{user.nickname}}</h1></td>
    </tr>
</table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}
```
Once we have a fully functioning index page we will invoke this same sub-
template from over there, but we aren't quite ready to do that, we'll
leave that for a future chapter of this tutorial.
More interesting profiles

More interesting profiles

While we now have a nice profile page, we don't really have much
information to show on it. Users like to tell a bit about them on these
pages, so we'll let them write something about themselves that we can
show here. We will also keep track of what was the last time each user
accessed the site, so that we can also show that on their profile page.

To add these things we have to start by modifying our database. More
specifically, we need to add two new fields to our User class (file
app/models.py):
```
class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    nickname = db.Column(db.String(64), unique = True)
    email = db.Column(db.String(120), index = True, unique = True)
    role = db.Column(db.SmallInteger, default = ROLE_USER)
    posts = db.relationship('Post', backref = 'author', lazy =
'dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)
```
Every time we modify the database we have to generate a new migration.
Remember that in the database chapter we went through the pain of
setting up a database migration system. We can see the fruits of that
effort now. To add these two new fields to our database we just do this:
```
./db_migrate.py
```
To which our script will respond:

New migration saved as db_repository/versions/003_migration.py
Current database version: 3
And our two new fields are now in our database. Remember that if you are
on Windows the way to invoke this script is different.

If we did not have migration support you would have needed to edit your
database manually, or worse, delete it and recreate it from scratch.

Next, let's modify our profile page template to show these fields (file
app/templates/user.html):
```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td>
            <h1>User: {{user.nickname}}</h1>
            {% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
            {% if user.last_seen %}<p><i>Last seen on:
{{user.last_seen}}</i></p>{% endif %}
        </td>
    </tr>
</table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}
```
Note we make use of Jinja2's conditionals to show these fields, because
we only want to show them when they are set.

At this point these two new fields are empty for all users, so nothing
will show.

The last_seen field is pretty easy to support. Remember that in a
previous chapter we created a before_request handler, to register the
logged in user with the flask.g global, as g.user. That is the perfect
time to update our database with the last access time for a user (file
app/views.py):
```
from datetime import datetime
# ...
@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated():
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()
```
If you log in to your profile page again the last seen time will now
display, and each time you refresh the page the time will update as
well, because each time the browser makes a request the before_request
handler will update the time in the database.

Note that we are writing the time in the standard UTC timezone. We
discussed this in a previous chapter, we will write all timestamps in
UTC so that they are consistent. That has the undesired side effect that
the time displayed in the user profile page is also in UTC. We will fix
this in a future chapter that will be dedicated to date and time
handling.

To display the user's about me information we have to give them a place
to enter it, and the proper place for this is in the edit profile page.
Editing the profile information

Editing the profile information

Adding a profile form is surprisingly easy. We start by creating the web
form (file app/forms.py):
```
from flask.ext.wtf import Form, TextField, BooleanField, TextAreaField
from flask.ext.wtf import Required, Length

class EditForm(Form):
    nickname = TextField('nickname', validators = [Required()])
    about_me = TextAreaField('about_me', validators = [Length(min = 0,
max = 140)])
```
Then the view template (file app/templates/edit.html):
```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>Edit Your Profile</h1>
<form action="" method="post" name="edit">
    {{form.hidden_tag()}}
    <table>
        <tr>
            <td>Your nickname:</td>
            <td>{{form.nickname(size = 24)}}</td>
        </tr>
        <tr>
            <td>About yourself:</td>
            <td>{{form.about_me(cols = 32, rows = 4)}}</td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="Save Changes"></td>
        </tr>
    </table>
</form>
{% endblock %}
```
And finally we write the view function (file app/views.py):
```
from forms import LoginForm, EditForm

@app.route('/edit', methods = ['GET', 'POST'])
@login_required
def edit():
    form = EditForm()
    if form.validate_on_submit():
        g.user.nickname = form.nickname.data
        g.user.about_me = form.about_me.data
        db.session.add(g.user)
        db.session.commit()
        flash('Your changes have been saved.')
        return redirect(url_for('edit'))
    else:
        form.nickname.data = g.user.nickname
        form.about_me.data = g.user.about_me
    return render_template('edit.html',
        form = form)
```
To make this page easy to reach, we also add a link to it from the user
profile page (file app/templates/user.html):
```
<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td>
            <h1>User: {{user.nickname}}</h1>
            {% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
            {% if user.last_seen %}<p><i>Last seen on:
{{user.last_seen}}</i></p>{% endif %}
            {% if user.id == g.user.id %}<p><a
href="{{url_for('edit')}}">Edit</a></p>{% endif %}
        </td>
    </tr>
</table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}
```
Pay attention to the clever conditional we are using to make sure that
the Edit link appears when you are viewing your own profile, but not
when you are viewing someone else's.