Skip to content

Commit 84e5b8c

Browse files
author
tomasrasymas
committed
init
0 parents  commit 84e5b8c

13 files changed

+441
-0
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
.ipynb_checkpoints
3+
__pycache__
4+
.idea
5+
*.pyc
6+
/dist/
7+
/*.egg-info

README.md

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Flask-RESTful API project template
2+
3+
This project shows one of the possible ways to implement RESTful API server.
4+
5+
There are implemented two models: User and Todo, one user has many todos.
6+
7+
Main libraries used:
8+
1. Flask-Migrate - for handling all database migrations.
9+
2. Flask-RESTful - restful API library.
10+
3. Flask-Script - provides support for writing external scripts.
11+
4. Flask-SQLAlchemy - adds support for SQLAlchemy ORM.
12+
13+
Project structure:
14+
```
15+
.
16+
├── README.md
17+
├── app.py
18+
├── endpoints
19+
│   ├── __init__.py
20+
│   ├── todos
21+
│   │   ├── __init__.py
22+
│   │   ├── model.py
23+
│   │   └── resource.py
24+
│   └── users
25+
│   ├── __init__.py
26+
│   ├── model.py
27+
│   └── resource.py
28+
├── manage.py
29+
├── requirements.txt
30+
└── settings.py
31+
```
32+
33+
* endpoints - holds all endpoints.
34+
* app.py - flask application initialization.
35+
* settings.py - all global app settings.
36+
* manage.py - script for managing application (migrations, server execution, etc.)
37+
38+
## Running
39+
40+
1. Clone repository.
41+
2. pip install requirements.txt
42+
3. Run following commands:
43+
1. python manage.py db init
44+
2. python manage.py db migrate
45+
3. python manage.py db upgrade
46+
4. Start server by running python manage.py runserver
47+
48+
## Usage
49+
### Users endpoint
50+
POST http://127.0.0.1:5000/api/users
51+
52+
REQUEST
53+
```json
54+
{
55+
"name": "John John"
56+
}
57+
```
58+
RESPONSE
59+
```json
60+
{
61+
"id": 1,
62+
"name": "John John",
63+
"todos": []
64+
}
65+
```
66+
PUT http://127.0.0.1:5000/api/users/1
67+
68+
REQUEST
69+
```json
70+
{
71+
"name": "Smith Smith"
72+
}
73+
```
74+
RESPONSE
75+
```json
76+
{
77+
"id": 1,
78+
"name": "Smith Smith",
79+
"todos": []
80+
}
81+
```
82+
DELETE http://127.0.0.1:5000/api/users/1
83+
84+
RESPONSE
85+
```json
86+
{
87+
"id": 3,
88+
"name": "Tom Tom",
89+
"todos": []
90+
}
91+
```
92+
GET http://127.0.0.1:5000/api/users
93+
94+
RESPONSE
95+
```json
96+
{
97+
"count": 2,
98+
"users": [
99+
{
100+
"id": 1,
101+
"name": "John John",
102+
"todos": [
103+
{
104+
"id": 1,
105+
"name": "First task",
106+
"description": "First task description"
107+
},
108+
{
109+
"id": 2,
110+
"name": "Second task",
111+
"description": "Second task description"
112+
}
113+
]
114+
},
115+
{
116+
"id": 2,
117+
"name": "Smith Smith",
118+
"todos": []
119+
}
120+
]
121+
}
122+
```
123+
GET http://127.0.0.1:5000/api/users/2
124+
```json
125+
{
126+
"id": 2,
127+
"name": "Smith Smith",
128+
"todos": []
129+
}
130+
```
131+
GET http://127.0.0.1:5000/api/users?name=John John
132+
```json
133+
{
134+
"count": 1,
135+
"users": [
136+
{
137+
"id": 1,
138+
"name": "John John",
139+
"todos": [
140+
{
141+
"id": 1,
142+
"name": "First task",
143+
"description": "First task description"
144+
},
145+
{
146+
"id": 2,
147+
"name": "Second task",
148+
"description": "Second task description"
149+
}
150+
]
151+
}
152+
]
153+
}
154+
```
155+
GET http://127.0.0.1:5000/api/users?limit=1&offset=1
156+
```json
157+
{
158+
"count": 1,
159+
"users": [
160+
{
161+
"id": 2,
162+
"name": "Smith Smith",
163+
"todos": []
164+
}
165+
]
166+
}
167+
```
168+
169+
Todo endpoint is similar to Users endpoint.

app.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from flask import Flask, jsonify
2+
from flask_restful import Api
3+
from flask_sqlalchemy import SQLAlchemy
4+
from werkzeug.exceptions import HTTPException
5+
from werkzeug.exceptions import default_exceptions
6+
import settings
7+
8+
app = Flask(__name__)
9+
10+
11+
@app.errorhandler(Exception)
12+
def handle_error(e):
13+
code = 500
14+
if isinstance(e, HTTPException):
15+
code = e.code
16+
return jsonify(error=str(e)), code
17+
18+
for ex in default_exceptions:
19+
app.register_error_handler(ex, handle_error)
20+
21+
22+
app.config['SQLALCHEMY_DATABASE_URI'] = settings.SQLALCHEMY_DATABASE_URI
23+
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = settings.SQLALCHEMY_TRACK_MODIFICATIONS
24+
app.config['BUNDLE_ERRORS'] = settings.BUNDLE_ERRORS
25+
26+
db = SQLAlchemy(app)
27+
api = Api(app)
28+
api.prefix = '/api'
29+
30+
from endpoints.users.resource import UsersResource
31+
from endpoints.todos.resource import TodosResource
32+
33+
api.add_resource(UsersResource, '/users', '/users/<int:user_id>')
34+
api.add_resource(TodosResource, '/todos', '/todos/<int:todo_id>')
35+
36+
if __name__ == '__main__':
37+
app.run()

endpoints/__init__.py

Whitespace-only changes.

endpoints/todos/__init__.py

Whitespace-only changes.

endpoints/todos/model.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from app import db
2+
3+
4+
class Todo(db.Model):
5+
__tablename__ = 'todo'
6+
7+
id = db.Column(db.Integer, primary_key=True)
8+
name = db.Column(db.String(20))
9+
description = db.Column(db.String(100))
10+
11+
user_id = db.Column(db.Integer, db.ForeignKey('user.id'),
12+
nullable=False)
13+
14+
def __repr__(self):
15+
return 'Id: {}, name: {}'.format(self.id, self.name)

endpoints/todos/resource.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from flask_restful import Resource, reqparse, request
2+
from flask_restful import fields, marshal_with, marshal
3+
from .model import Todo
4+
from app import db
5+
6+
todo_fields = {
7+
'id': fields.Integer,
8+
'name': fields.String,
9+
'description': fields.String,
10+
'user_id': fields.Integer
11+
}
12+
13+
todo_list_fields = {
14+
'count': fields.Integer,
15+
'todos': fields.List(fields.Nested(todo_fields)),
16+
}
17+
18+
todo_post_parser = reqparse.RequestParser()
19+
todo_post_parser.add_argument('name', type=str, required=True, location=['json'],
20+
help='name parameter is required')
21+
todo_post_parser.add_argument('description', type=str, required=True, location=['json'],
22+
help='description parameter is required')
23+
todo_post_parser.add_argument('user_id', type=int, required=True, location=['json'],
24+
help='user_id parameter is required')
25+
26+
27+
class TodosResource(Resource):
28+
def get(self, todo_id=None):
29+
if todo_id:
30+
todo = Todo.query.filter_by(id=todo_id).first()
31+
return marshal(todo, todo_fields)
32+
else:
33+
args = request.args.to_dict()
34+
limit = args.get('limit', 0)
35+
offset = args.get('offset', 0)
36+
37+
args.pop('limit', None)
38+
args.pop('offset', None)
39+
40+
todo = Todo.query.filter_by(**args).order_by(Todo.id)
41+
if limit:
42+
todo = todo.limit(limit)
43+
44+
if offset:
45+
todo = todo.offset(offset)
46+
47+
todo = todo.all()
48+
49+
return marshal({
50+
'count': len(todo),
51+
'todos': [marshal(t, todo_fields) for t in todo]
52+
}, todo_list_fields)
53+
54+
@marshal_with(todo_fields)
55+
def post(self):
56+
args = todo_post_parser.parse_args()
57+
58+
todo = Todo(**args)
59+
db.session.add(todo)
60+
db.session.commit()
61+
62+
return todo
63+
64+
@marshal_with(todo_fields)
65+
def put(self, todo_id=None):
66+
todo = Todo.query.get(todo_id)
67+
68+
if 'name' in request.json:
69+
todo.name = request.json['name']
70+
71+
if 'description' in request.json:
72+
todo.description = request.json['description']
73+
74+
db.session.commit()
75+
return todo
76+
77+
@marshal_with(todo_fields)
78+
def delete(self, todo_id=None):
79+
todo = Todo.query.get(todo_id)
80+
81+
db.session.delete(todo)
82+
db.session.commit()
83+
84+
return todo

endpoints/users/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from flask import Blueprint
2+
3+
users_blueprint = Blueprint('users', __name__)

endpoints/users/model.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from app import db
2+
3+
4+
class User(db.Model):
5+
__tablename__ = 'user'
6+
7+
id = db.Column(db.Integer, primary_key=True)
8+
name = db.Column(db.String(20))
9+
10+
todos = db.relationship('Todo', backref='user', lazy='select')
11+
12+
def __repr__(self):
13+
return 'Id: {}, name: {}'.format(self.id, self.name)

0 commit comments

Comments
 (0)