https://techarena51.com/index.php/how-to-build-database-driven-restful-api-applications-with-flask-flask-restful-and-angularjss-resource-service
        
        
                
                            
                        
In this tutorial I will describe how you can Build a RESTFUL API CRUD(Create, Read, Update and Delete) application with the Flask Python Micro-framework, and the Angularjs $resource Service. I be will using the Flask-RESTFUL API extension to create an API backend and then use Angularjs’s $resource service to communicate with it.
Prerequisites
This Tutorial is for users who are familiar with the Flask Python and Angularjs Javascript Web frameworks.
Minimum System/Software Requirements that you need to have are:
Ubuntu Linux 14.04
Python 3.4
Flask 0.10
Angularjs 1.4
PostgreSQL 9+ or MySQL/Mariadb 5+
Part 1: Installing and Configuring a Flask RESTFUL API Server.
The first step is to download Flask, Flask-RESTFUL and Flask-SQLAlchemy along with their dependencies into a virtual environment and configure them to connect to your database.
Next using the Flask-SQLAlchemy ‘Model Class ‘ create Table and Column Definitions as follows:
In the above code example I have also included the Schema class from 
the ‘marshmallow_jsonapi’ python module to send JSON formatted data as 
per the JSON specification at jsonapi.org and the validation class from 
the ‘marshmallow’ python module to validate Data. Refer to the links at 
the bottom of this article to learn more about these modules.
Next create a ‘controller.py’ file and define the CRUD Classes and API endpoints using the Flask-RESTful flask extension as follows:
The above code will first validate incoming data using the 
‘Marshmallow Schema Class’ that was defined in our model and only then 
read or update data in the database. Next initialize the Flask 
Application with the SQLAlchemy database object.
vim app/__init__.py
In the above file I have also defined the API URL ‘/api/v1/users’. 
This URL is as per the WhiteHouse RESTFUL API guidelines on Github where
 ‘v1’ is the version number and ‘users’ is the resource. The CRUD API 
calls need to be to made to /api/v1/users.json and 
/api/v1/users/{id}.json for all and single user respectively.
Finally create the database tables and columns with the Flask-Migrations extension as follows:
vim migrate.py
Part 2 : Making CRUD RESTFUL API calls with the Angularjs $Resource Service
The Angularjs $Resource Service provides an easy way to make RESTFUL API calls without having to define each method. It has the HTTP methods defined and available by default, you can also define your own method if required.
Along with $resource service I have also used the ‘ui-router’ module for routing.
With ‘ui-router’ I can define states for each of the CRUD routes except for DELETE and then use them to transition between states. You can check the demo on my Application for a live example of how they work. You can also refer to the link at the end of this article for more information
Lastly create the relevant HTML pages to Add, View and Update your Application.
                        In this tutorial I will describe how you can Build a RESTFUL API CRUD(Create, Read, Update and Delete) application with the Flask Python Micro-framework, and the Angularjs $resource Service. I be will using the Flask-RESTFUL API extension to create an API backend and then use Angularjs’s $resource service to communicate with it.
Prerequisites
This Tutorial is for users who are familiar with the Flask Python and Angularjs Javascript Web frameworks.
Minimum System/Software Requirements that you need to have are:
Ubuntu Linux 14.04
Python 3.4
Flask 0.10
Angularjs 1.4
PostgreSQL 9+ or MySQL/Mariadb 5+
Part 1: Installing and Configuring a Flask RESTFUL API Server.
The first step is to download Flask, Flask-RESTFUL and Flask-SQLAlchemy along with their dependencies into a virtual environment and configure them to connect to your database.
#Create a Flask project directory mkdir -p myproject/app cd myproject #create and activate the virtual environment virtualenv -p /usr/bin/python3.4 venv-3.4 source venv-3.4/bin/activate #Install Flask, and FLask-SQLAlchemy along with PostgreSQL and MySQL database drivers pip install flask flask-sqlalchemy marshmallow-jsonapi marshmallow psycopg2 pymysql flask-restful #You can also clone the requirements file from #https://github.com/Leo-G/Flask-Scaffold/blob/master/requirements.txt #Create a config file and add your database string vim config.py
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 
 | # DATABASE SETTINGSpg_db_username = 'postgres'pg_db_password = ''pg_db_name = 'flask-angularjs-api’'pg_db_hostname = 'localhost'# MYSQLmysql_db_username = 'root'mysql_db_password = ''mysql_db_name = 'flask-angularjs-api’'mysql_db_hostname = 'localhost'SQLALCHEMY_TRACK_MODIFICATIONS = True# PostgreSQL# Comment the below string if you are using MySQL or MariaDBSQLALCHEMY_DATABASE_URI = "postgresql://{DB_USER}:{DB_PASS}@{DB_ADDR}/{DB_NAME}".format(DB_USER=pg_db_username,                                                                                        DB_PASS=pg_db_password,                                                                                        DB_ADDR=pg_db_hostname,                                                                                        DB_NAME=pg_db_name)# MySQL# Uncomment the below string if you are using MySQL or MariaDB"""SQLALCHEMY_DATABASE_URI = "mysql+pymysql://{DB_USER}:{DB_PASS}@{DB_ADDR}/{DB_NAME}".format(DB_USER=mysql_db_username,                                                                                        DB_PASS=mysql_db_password,                                                                                        DB_ADDR=mysql_db_hostname,                                                                                        DB_NAME=mysql_db_name)""" | 
vim app/models.py
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 
 | from marshmallow_jsonapi import Schema, fieldsfrom marshmallow import validatefrom flask.ext.sqlalchemy import SQLAlchemydb = SQLAlchemy()class Users(db.Model):    id = db.Column(db.Integer, primary_key=True)    name = db.Column(db.String(250), unique=True, nullable=False)    address = db.Column(db.Text, nullable=False)    age = db.Column(db.Integer, nullable=False)    profession = db.Column(db.String(250), nullable=False)    website = db.Column(db.String(250), nullable=False)    def __init__(self,  name,  address,  age,  profession, website):        self.name = name        self.address = address        self.age = age        self.profession = profession        self.website = website             def add(self, resource):        db.session.add(resource)        return db.session.commit()    def update(self):        return db.session.commit()    def delete(self, resource):        db.session.delete(resource)        return db.session.commit()     class UsersSchema(Schema):    not_blank = validate.Length(min=1, error='Field cannot be blank')    # add validate=not_blank in required fields    id = fields.Integer(dump_only=True)       name = fields.String(validate=not_blank)    address = fields.String(validate=not_blank)    age = fields.Integer(required=True)    profession = fields.String(validate=not_blank)    website = fields.String(validate=not_blank)    # self links    def get_top_level_links(self, data, many):        if many:            self_link = "/users/"        else:            self_link = "/users/{}".format(data['id'])        return {'self': self_link}            #The below type object is a resource identifier object as per               http://jsonapi.org/format/#document-resource-identifier-objects    class Meta:        type_ = 'users' | 
Next create a ‘controller.py’ file and define the CRUD Classes and API endpoints using the Flask-RESTful flask extension as follows:
vim app/controller.py
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 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
 | from flask import Blueprint, request, jsonify, make_responsefrom flask_restful import Api, Resourcefrom app.models import db,Users, UsersSchemafrom sqlalchemy.exc import SQLAlchemyErrorfrom marshmallow import ValidationError#Initialize a Flask Blueprint,users = Blueprint('users', __name__)#Initialize the UserSchema we defined in models.pyschema = UsersSchema(strict=True)#Initialize the  API  object using the Flask-RESTful API classapi = Api(users)# Create CRUD classes using the Flask-RESTful Resource classclass CreateListUsers(Resource):        def get(self):        users_query = Users.query.all()        results = schema.dump(users_query, many=True).data        return results    def post(self):        raw_dict = request.get_json(force=True)        try:            schema.validate(raw_dict)            request_dict = raw_dict['data']['attributes']            print(raw_dict)            user = Users(request_dict['name'], request_dict['address'], request_dict['age'], request_dict[                         'profession'], request_dict['website'])            user.add(user)            # Should not return password hash            query = Users.query.get(user.id)            results = schema.dump(query).data            return results, 201        except ValidationError as err:            resp = jsonify({"error": err.messages})            resp.status_code = 403            return resp        except SQLAlchemyError as e:            db.session.rollback()            resp = jsonify({"error": str(e)})            resp.status_code = 403            return respclass GetUpdateDeleteUser(Resource):        def get(self, id):        user_query = Users.query.get_or_404(id)        result = schema.dump(user_query).data        return result    def patch(self, id):        user = Users.query.get_or_404(id)        raw_dict = request.get_json(force=True)        try:            schema.validate(raw_dict)            request_dict = raw_dict['data']['attributes']            for key, value in request_dict.items():                setattr(user, key, value)            user.update()            return self.get(id)        except ValidationError as err:            resp = jsonify({"error": err.messages})            resp.status_code = 401            return resp        except SQLAlchemyError as e:            db.session.rollback()            resp = jsonify({"error": str(e)})            resp.status_code = 401            return resp    def delete(self, id):        user = Users.query.get_or_404(id)        try:            delete = user.delete(user)            response = make_response()            response.status_code = 204            return response        except SQLAlchemyError as e:            db.session.rollback()            resp = jsonify({"error": str(e)})            resp.status_code = 401            return resp#Map classes  to API enpointsapi.add_resource(CreateListUsers, '.json')api.add_resource(GetUpdateDeleteUser, '/<int:id>.json') | 
vim app/__init__.py
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 
 | from flask import Flaskdef create_app(config_filename):    app = Flask(__name__, static_folder='templates/static')    app.config.from_object(config_filename)                 #Init Flask-SQLAlchemy    from app.models import db    db.init_app(app)    from app.controller import users    app.register_blueprint(users, url_prefix='/api/v1/users')         from flask import render_template, send_from_directory             @app.route('/<path:filename>')    def file(filename):        from os import path        return send_from_directory(path.join(app.root_path, 'templates'), filename)             @app.route('/')    def index():        return render_template('index.html')             return app | 
Finally create the database tables and columns with the Flask-Migrations extension as follows:
vim migrate.py
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
 | from flask.ext.migrate import Migrate, MigrateCommandfrom config import SQLALCHEMY_DATABASE_URIfrom app.models import dbfrom flask import Flaskfrom flask.ext.script import Managerfrom app import create_appapp = create_app('config')migrate = Migrate(app, db)manager = Manager(app)manager.add_command('db', MigrateCommand)  if __name__ == '__main__':    manager.run() | 
#run the following commands to perform the actual database migrations Python migrate.py db init Python migrate.py db migrate Python migrate.py db upgradeRun the application using Flask’s inbuilt web server as follows:
vim run.py
1 
2 
3 
4 
5 
6 
7 
8 
9 
 | #!/usr/bin/env pythonfrom app import create_appapp = create_app('config')if __name__ == '__main__':    app.run(host='0.0.0.0',            port=5000,            debug='true') | 
python run.pyBelow is a small demo video using the PostMan API client of the REST API calls
Part 2 : Making CRUD RESTFUL API calls with the Angularjs $Resource Service
The Angularjs $Resource Service provides an easy way to make RESTFUL API calls without having to define each method. It has the HTTP methods defined and available by default, you can also define your own method if required.
mkdir -p app/templates/static/js/ vim app/templates/static/js/app.js
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 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
 | //Initialize an Angularjs Applicationvar app =angular.module('myApp', ['ui.router','ngResource', 'myApp.controllers', 'myApp.services', 'toaster']);// Create a User Resource Object using the resource serviceangular.module('myApp.services', []).factory('User', function($resource) {  return $resource('api/v1/users/:id.json', { id:'@users.id' }, {    update: {      method: 'PATCH',    }    }, {    stripTrailingSlashes: false    });});// Create routes with UI-Router and display the appropriate HTML file for listing, adding or updating usersangular.module('myApp').config(function($stateProvider, $urlRouterProvider) {  //  // For any unmatched url, redirect to /state1  $urlRouterProvider.otherwise("/");      $stateProvider      .state('users', {               abstract: true,        url: '/',        title: 'Users',        template: '<ui-view/>'    })  .state('users.list', {    url: 'list',    templateUrl: 'list.html',    controller: 'UserListController',        }).state('users.add', {    url: 'add',    templateUrl: 'add.html',    controller: 'UserCreateController',      }).state('users.edit', {    url: ':id/edit',    templateUrl: 'update.html',    controller: 'UserEditController',            })});// Define CRUD controllers to make the add, update and delete calls using the User resource we defined earlierangular.module('myApp.controllers', []).controller('UserListController', function($scope, User, $state, toaster) {        User.get(function(data) {// Get all the users. Issues a GET to /api/v1/users.json                                                             $scope.users = [];                     angular.forEach(data.data, function(value, key)                                                        {                                                       this.user = value.attributes;                                                       this.user['id'] = value.id;                                                       this.push(this.user);                                                        },   $scope.users);                                                                  },                function(error){                     toaster.pop({                            type: 'error',                            title: 'Error',                            body: error,                            showCloseButton: true,                            timeout: 0                            });                                              });   $scope.deleteUser = function(selected_id) { // Delete a User. Issues a DELETE to /api/v1/users/{user-id}.json      user = User.get({ id: selected_id});      user.$delete({ id: selected_id},function() {        toaster.pop({                type: 'success',                title: 'Success',                body: "User deleted successfully",                showCloseButton: true,                timeout: 0                });        $state.go('users.list'); //redirect to home on a successful deletion        $state.reload();               }, function(error) {         toaster.pop({                type: 'error',                title: 'Error',                body: error,                showCloseButton: true,                timeout: 0                });    });    };  }).controller('UserCreateController', function($scope, User, $state, toaster) {          $scope.user = new User();  //create new user.                  $scope.addUser = function() {                                                                 $scope.user.data.type = "users";                                $scope.user.$save(function() { // Add a User. Issues a POST to /api/v1/users.json                                toaster.pop({                                            type: 'success',                                            title: 'Success',                                            body: "User saved successfully",                                            showCloseButton: true,                                            timeout: 0                                             });                                                                        $state.go('users.list'); // on success go back home                                }, function(error) {                               toaster.pop({                                            type: 'error',                                            title: 'Error',                                            body: error,                                            showCloseButton: true,                                            timeout: 0                                            });                                                                                                         });}}).controller('UserEditController', function($scope, $state, $stateParams, toaster, $window,  User) {            $scope.updateUser = function() { //Update the user. Issues a PATCH to /api/v1/users/{user-id}.json    $scope.user.$update({ id: $stateParams.id },function() {     toaster.pop({                type: 'success',                title: 'Success',                body: "Update was a success",                showCloseButton: true,                timeout: 0                });        $state.go('users.list'); // on success go back to home             }, function(error) {    toaster.pop({                type: 'error',                title: 'Error',                body: error,                showCloseButton: true,                timeout: 0                });      });  };  $scope.loadUser = function() { //Issues a GET request to /api/v1/users/{user-id}.json. Fetches a single user                       $scope.user = User.get({ id: $stateParams.id },                                       function() {}, function(error) {                                          toaster.pop({                                                type: 'error',                                                title: 'Error',                                                body: error,                                                showCloseButton: true,                                                timeout: 0                                                });                                                });                                };  $scope.loadUser();  });//Tutorial at Techarena51.com | 
With ‘ui-router’ I can define states for each of the CRUD routes except for DELETE and then use them to transition between states. You can check the demo on my Application for a live example of how they work. You can also refer to the link at the end of this article for more information
Lastly create the relevant HTML pages to Add, View and Update your Application.
vim app/templates/index.html
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 
58 
59 
60 
 |                                | 
"myApp" > 
"main">
 
 
class="nav">
 
 
 
 
 
 
 
 
 
 
                             
                        
           
  
                                        vim app/templates/list.html
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 
 | 
    | 
"user in users"> 
{{ user.name }} {{ user.address }} {{ user.age}} {{ user.profession }} {{ user.website}}         vim app/templates/form.html
1 
2 
3 
4 
5 
6 
 |                   | 
vim app/templates/add.html
1 
2 
3 
4 
5 
6 
 | 
            | 
vim app/templates/update.html
1 
2 
3 
4 
5 
6 
7 
 | 
                | 
python run.pyBelow is a video of a working Application
The complete code is available at https://github.com/Leo-G/Flask-Scaffold/. An easier way to build CRUD Applications with Flask and Angularjs is with Flask-Scaffold. Just provide your Database definitions in a YAML file and it will generate all the code including the valiadations for you. The instructions are at https://github.com/Leo-G/Flask-Scaffold/blob/master/README.md
You can also add Authentication to your API Application with JSON Token Based Authentication.
Reference:
https://docs.angularjs.org/api/ngResource/service/$resource
https://github.com/angular-ui/ui-router
https://github.com/WhiteHouse/api-standards
http://flask-restful-cn.readthedocs.io/en/0.3.4/
http://marshmallow.readthedocs.io/en/latest/

No comments:
Post a Comment