Flask Biography Tutorial Part IX : Creating Product Portofolio Page

 

A compact layout of our Portfolio table

Figure IX-1 Portofolio grid in Bio Page complete with action buttons

 

Before we going further on our journey of making a fully functional Bio Application, lets return to our early concept of this application : product portfolio show case. And guess what we missed. Correct. We missed the required application model for a product portfolio. If you rethink our application models until this state, we know we haven't add a portfolio model/table. This part of article series will guide you on how exactly it's done by adding Portfolio model using SQLAlchemy relationship field in our existing Users mode.

Before diving deep into our application model, let us start by perfecting our already established bio.html template. Figure IX-1 shows the latest enhancement of our Product Portfolio element reside in bio.html template. We had add an Add button to, well, you know, adding new product portfolio to the list. And a couple of Edit and Delete button before product portfolio title page, to edit and delete portfolio in that line. Pretty basic. But, it will makes our Bio Application getting close to its original intention. 

Another design decision we are going to make is, "What are we going to do if users click our (again) persuasive Add button?" Shall we present new html page in the current browser window, or simply popping it out in the same window but within a modal dialog box? Well, of course you know the answer. Let's just pop it.

Modal dialog box of Portofolio form

Informative and concise. Let's not making our users life any harder

 

Having a modal dialog box, require us to add another interesting feature to our application : client side validation using jQuery. Whether this feature is secure or not, is a debatable matter , [2]. But there are two important things where a client side validation become a must in a popup dialog box like this:

  1. As the validation is done using Ajax call, the dialog doesn't have to be closed and reopened upon user submitting of the form. It will be useless if we popup a dialog box and then the validation must be done by closing the form, calling server request using POST method, displaying previous page and then popping out the dialog box again.
  2. User enjoyment! Upon tabbing out of the current input field, user will have immediate validation feedback. This surely will make our user enjoy working with our application even more.

Great!

Now that we have already laying out the foundation of our new application feature, let's tackle its development step-by-step

A. Adding Relationship Field in Users class

How do we add portfolio data for each registered user in our application? Anyway, what do I mean by Portfolio? Well, as I live on a Mac OS X environment, I can easily hit CMD+SPACE and type : "portfolio"... and here goes one definition of portfolio.

One clear definition of Portfolio

One clear definition of Portfolio

Indeed, we are going to add set of pieces of our users creative work to demonstrate his/her ability to a potential employer. As I said in the early beginning of this tutorial series, this Biography application is some kind of elegant combination between about.me and LinkedIn. Let me know whether I strive in the right direction!

Well, our next question is : How do we add Portfolio data for each of our registered users? A veteran in database application development will know that, the thing that this application need is a one-to-many table relationship between existing Users class and a yet-to-be-added Portfolio class. And not just that, we also need to know: How do we do this in SQLAlchemy?

Let start with an Entity-Relationship Diagram between Users  and Portfolio.

 

A one-to-many relationship between Users (one) and Portfolio (many)

A one-to-many relationship between Users (one) and Portfolio (many)

The diagram above means that, for each one single item in Users class, there may be many item in Portfolio class, which related to that single item in Users class . Remember that, we are talking using ORM tools in this context, so the implementation part maybe different from one ORM tool to another. Here, we are going to implement it using SQLAlchemy.

Let start with what we need to change in our existing Users class.

1
2
3
4
class Users(db.Model):
..
  portfolio = db.relationship('Portfolio')
..

We only need to add one single portfolio field with argument Portfolio, which will be the name for the new child class. Here is the new  Portfolio class itself:

1
2
3
4
5
6
7
8
9
10
class Portfolio(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  title = db.Column(db.String(60), unique=True)
  description = db.Column(db.Text)
  tags = db.Column(db.Text)
  user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
  def __init__(self, title = None, description = None, tags = None):
    self.title = title
    self.description = description
    self.tags = tags

That's all the new code!

In Portfolio class the only field that is special is user_id field that get instantiated using db.ForeignKey argument. It expect the name of its parent class (which is Users) primary key (which is id).

And how do we use this newly added relationship field to add sets of portfolio for certain user? Let see our newly modified dbinit() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def dbinit():
  ...
  user = Users(username='ekowibowo', firstname='Eko', 
        lastname='Suprapto Wibowo', password=hash_string('rahasia'),
        email='swdev.bali@gmail.com', 
        tagline='A cool coder and an even cooler Capoeirista', 
        bio = 'I love Python very much!', 
        avatar = '/static/avatar.png',
        active = True)
  user.portfolio.append(Portfolio(title = 'FikrPOS',
        description = 'An integrated POS solution using cloud concept',
        tags='python,c#,openshift,flask,sqlalchemy,postgresql,bootstrap3'))
  user.portfolio.append(Portfolio(title = 'Bio Application',
        description = 'An autobiography publisher', 
        tags='python,openshift,flask,sqlalchemy,postgresql,bootstrap3'))

This code changes implement a correct implementation of one-to-many relationship between Users class and Portofolio table. A very elegant way of implementing it!

To summarize it, the only changes that we need in existing Users class is to add portfolio = db.relationship('Portfolio') field. While in the new Portfolio class we implement all the regular field (title,description, and tags) with one special field: user_id = db.Column(db.Integer, db.ForeignKey('users.id')). This field will link back to certain Portofolio  data to its rightful owner in Users class. To use this new relationship, we simply call portfolio.append method and passing a new instance of Portfolio class. Pretty basic!

B. Enriching and Displaying Portfolio Table

 

If we talk about page layout design decision, it may become an endless talk. Designer may come up with various way on how to perfectly laying out a page. Thanks to Bootstrap, that issue can be solved easily : 12 column layout. After testing several Bootstrap 12 columns layout configuration, I quite satisfied with the current layout, where each elements is not overlapped each other when window size was changed.

Here, we will dive into the technique of creating a functional Bootstrap3 table for our Portfolio. Recall that our Bio application was designed with theme customization in mind, so the template that responsible for rendering our bio page was stored in wsgi/templates/themes/water/bio.html.  Have a look at Figure IX-1 where I depicted a compact view of Portfolio table in a bio page. And have a look at its HTML code below.  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<div class="page-header">
<h1>{{ user.firstname }} {{ user.lastname }}</h1>
 
<strong>{{ user.tagline }}</strong>
 
</div>
<div class="row">
<div class="col-sm-4">
<div align="center" class="container">
<img class="img img-rounded" src="http://pythonthusiast.pythonblogs.com/%7B%7B%20user.avatar%20%7D%7D" />
<p style="text-align: center">
{{ user.bio }}
 
</div>
</div>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-12">
<div class="navbar-left">
</div>
<form class="navbar-form navbar-right">
        <button class="btn btn-default btn-sm btn-success">Add</button>
        <div class="form-group">
        <input class="form-control" type="text" />
        </div>
        <button class="btn btn-default" type="submit"><span></span></button>
</form>
</div>
</div>
<div class="container">
<ul class="list-group">
        {% for porto in user.portfolio %}
        <li class="list-group-item">
        <div class="container">
        <div class="row">
        <div class="col-sm-5">
        <a href="#"><span></span></a> <a href="#"><span></span></a>
        <strong>
        {{ porto.title }}
        </strong>
        
        {{ porto.description}}
        
        </div>
        <div class="col-sm-7">
        {% for tag in porto.tags.split(',') %}
        <span>{{ tag }}</span>
        {% endfor %}
        </div>
        </div>
        </div>
        </li>
        {% endfor %}
</ul>
</div>
</div>
</div>

A general layout for the page consisted of dividing the page into a col-sm-4 to hold up user avatar + description and a col-sm-8 to hold user portfolio table. This  col-sm-8 will contained a full row of navbar consisting of Add button and search form and a container of  list-group list. Each list item will be another row having a  col-sm-5 to hold project title + description and a col-sm-7 to hold project tags showing technology being use.

To display product portfolio for certain user, we simply iterate user.portfolio  field using Jinja2 (% for %} statement. As for displaying the tags, we simply use split method in porto.tags field, and displaying those tags using Bootstrap 3 badge class. Pretty neat, right?

We don't need to change our route/view method that render our bio page, which is index() method. This is because to render bio.html our route/view method already pass user. And a default configuration of relationship field will automatically load its child data, which is portfolio. At the moment, it still suit to our needs. But for optimization purpose, we may later configure our Users.portfolio to use Eager Loading technique, which will not automatically load child data for Users class.

C. Using Modal Dialog Box for Portfolio Form

As we already decide to use Modal Dialog Box for adding new Portfolio data for certain user, our first step is to create an Add button that will trigger our modal dialog box. Have a look again in section B for our code that render Portfolio data, and here is the snippet:

1
<button class="btn btn-default btn-sm btn-success">Add</button>

In the above button element, we simply use data-toggle attribute to let it open a modal dialog box using id defined in data-target attribute. As we use porto_form as the id, here is the snippet of a div element that build our Portform as depicted in Figure IX-2.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<div class="modal fade" id="porto_form">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
</div>
</div>
</div>
<button class="close" type="button">&times;</button>
 
 
 
<h4 class="modal-title">Portofolio</h4>
<div class="modal-body">
<div class="container">
<div class="row">
<div class="col-sm-1">
</div>
<div class="col-sm-10">
{{ field definitions }}
</div>
<div class="col-sm-1">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary btn-success" type="submit">Save</button>
<button class="btn btn-default" type="button">Cancel</button>
</div>
</div>

We use modal class to note that the following div will be shown as a modal dialog box.

Building Client Side Validation  

Here, we will use jQuery Validation Plugin to implement client side validation of our Portfolio form. But, as I start writing article about it, I realize that this section will be quite complex and need its own article. Thereby, you will have to wait until Part X to start building our client-side validation technique.

Conclusion  

As always, code can be downloaded in : bio-part-9.zip.

And live application can be tested in http://bio-ekowibowo.rhcloud.com.

Stay tuned for the Part X of this article series!




Leave comments

authimage
  • I've tried multiple times to signup and log into http://bio-ekowibowo.rhcloud.com.
    It takes me to openshift.com
    I'd like to see the application tested. How can I do that?

    • Ed

Copyright(c) 2014 - PythonBlogs.com
By using this website, you signify your acceptance of Terms and Conditions and Privacy Policy
All rights reserved