Skip to content

Commit 4b27613

Browse files
committed
All in one working with tests
1 parent 5753dba commit 4b27613

File tree

7 files changed

+358
-151
lines changed

7 files changed

+358
-151
lines changed

db/.keep

Whitespace-only changes.

models.py

+73-52
Original file line numberDiff line numberDiff line change
@@ -24,50 +24,62 @@
2424
-----------
2525
name (string) - the name of the pet
2626
category (string) - the category the pet belongs to (i.e., dog, cat)
27+
available (boolean) - True for pets that are available for adoption
2728
2829
"""
29-
import threading
30+
import os
31+
import json
32+
import logging
33+
from flask_sqlalchemy import SQLAlchemy
3034

3135
class DataValidationError(Exception):
3236
""" Used for an data validation errors when deserializing """
3337
pass
3438

35-
class Pet(object):
39+
class Pet(db.Model):
3640
"""
3741
Class that represents a Pet
3842
39-
This version uses an in-memory collection of pets for testing
43+
This version uses arelational database for persistence
4044
"""
41-
lock = threading.Lock()
42-
data = []
43-
index = 0
45+
logger = logging.getLogger(__name__)
46+
Pet._db = None
4447

45-
def __init__(self, id=0, name='', category=''):
46-
""" Initialize a Pet """
47-
self.id = id
48-
self.name = name
49-
self.category = category
48+
# Table Schema
49+
id = db.Column(db.Integer, primary_key=True)
50+
name = db.Column(db.String(63))
51+
category = db.Column(db.String(63))
52+
available = db.Column(db.Boolean())
53+
54+
# def __init__(self, id=0, name='', category=''):
55+
# """ Initialize a Pet """
56+
# self.id = id
57+
# self.name = name
58+
# self.category = category
59+
60+
61+
def __repr__(self):
62+
return '<Pet %r>' % (self.name)
5063

5164
def save(self):
5265
"""
5366
Saves a Pet to the data store
5467
"""
55-
if self.id == 0:
56-
self.id = self.__next_index()
57-
Pet.data.append(self)
58-
else:
59-
for i in range(len(Pet.data)):
60-
if Pet.data[i].id == self.id:
61-
Pet.data[i] = self
62-
break
68+
if not self.id:
69+
Pet._db.session.add(self)
70+
Pet._db.session.commit()
6371

6472
def delete(self):
6573
""" Removes a Pet from the data store """
66-
Pet.data.remove(self)
74+
Pet._db.session.delete(self)
75+
Pet._db.session.commit()
6776

6877
def serialize(self):
6978
""" Serializes a Pet into a dictionary """
70-
return {"id": self.id, "name": self.name, "category": self.category}
79+
return {"id": self.id,
80+
"name": self.name,
81+
"category": self.category,
82+
"available": self.available}
7183

7284
def deserialize(self, data):
7385
"""
@@ -78,45 +90,51 @@ def deserialize(self, data):
7890
"""
7991
if not isinstance(data, dict):
8092
raise DataValidationError('Invalid pet: body of request contained bad or no data')
81-
# only update the id if it is zero
82-
# this prevents spoofing the id by changing the original id with the data
83-
if data.has_key('id') and self.id == 0:
84-
self.id = data['id']
8593
try:
8694
self.name = data['name']
8795
self.category = data['category']
88-
except KeyError as err:
89-
raise DataValidationError('Invalid pet: missing ' + err.args[0])
90-
return
96+
self.available = data['available']
97+
except KeyError as error:
98+
raise DataValidationError('Invalid pet: missing ' + error.args[0])
99+
except TypeError as error:
100+
raise DataValidationError('Invalid pet: body of request contained' \
101+
'bad or no data')
102+
return self
91103

92104
@staticmethod
93-
def __next_index():
94-
""" Generates the next index in a continual sequence """
95-
with Pet.lock:
96-
Pet.index += 1
97-
return Pet.index
105+
def init_db(app_db):
106+
""" Initializes the database session """
107+
Pet._db = app_db
108+
Pet.logger.info('Initializing database')
109+
Pet._db.create_all() # make our sqlalchemy tables
98110

99111
@staticmethod
100112
def all():
101113
""" Returns all of the Pets in the database """
102-
return [pet for pet in Pet.data]
103-
104-
@staticmethod
105-
def remove_all():
106-
""" Removes all of the Pets from the database """
107-
del Pet.data[:]
108-
Pet.index = 0
109-
return Pet.data
114+
Pet.logger.info('Processing all Pets')
115+
return Pet.query.all()
110116

111117
@staticmethod
112118
def find(pet_id):
113119
""" Finds a Pet by it's ID """
114-
if not Pet.data:
115-
return None
116-
pets = [pet for pet in Pet.data if pet.id == pet_id]
117-
if pets:
118-
return pets[0]
119-
return None
120+
Pet.logger.info('Processing lookup for id %s ...', pet_id)
121+
return Pet.query.get(pet_id)
122+
123+
@staticmethod
124+
def find_or_404(pet_id):
125+
""" Find a Pet by it's id """
126+
Pet.logger.info('Processing lookup or 404 for id %s ...', pet_id)
127+
return Pet.query.get_or_404(pet_id)
128+
129+
@staticmethod
130+
def find_by_name(name):
131+
""" Returns all Pets with the given name
132+
133+
Args:
134+
name (string): the name of the Pets you want to match
135+
"""
136+
Pet.logger.info('Processing name query for %s ...', name)
137+
return Pet.query.filter(Pet.name == name)
120138

121139
@staticmethod
122140
def find_by_category(category):
@@ -125,13 +143,16 @@ def find_by_category(category):
125143
Args:
126144
category (string): the category of the Pets you want to match
127145
"""
128-
return [pet for pet in Pet.data if pet.category == category]
146+
Pet.logger.info('Processing category query for %s ...', category)
147+
return Pet.query.filter(Pet.category == category)
129148

130149
@staticmethod
131-
def find_by_name(name):
132-
""" Returns all Pets with the given name
150+
def find_by_availability(available=True):
151+
""" Query that finds Pets by their availability """
152+
""" Returns all Pets by their availability
133153
134154
Args:
135-
name (string): the name of the Pets you want to match
155+
available (boolean): True for pets that are available
136156
"""
137-
return [pet for pet in Pet.data if pet.name == name]
157+
Pet.logger.info('Processing available query for %s ...', available)
158+
return Pet.query.filter(Pet.available == available)

requirements.txt

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
Flask==0.12
22
Flask-API==0.6.9
3-
# Testing
3+
Flask-SQLAlchemy==2.1
4+
SQLAlchemy==1.1.5
45
pylint
56
flake8
7+
8+
# Testing
69
mock==2.0.0
7-
httpie==0.9.9
810
nose==1.3.7
911
rednose==1.2.1
1012
pinocchio==0.4.2
13+
httpie==0.9.9
14+
1115
# Code coverage
1216
coverage==4.2
1317
codecov==2.0.5

server.py

+156-2
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,32 @@
3030
from flask import Flask, jsonify, request, url_for, make_response, abort
3131
from flask_api import status # HTTP Status Codes
3232
from werkzeug.exceptions import NotFound
33-
from models import Pet, DataValidationError
33+
#from models import Pet, DataValidationError
34+
35+
# For this example we'll use SQLAlchemy, a popular ORM that supports a
36+
# variety of backends including SQLite, MySQL, and PostgreSQL
37+
from flask_sqlalchemy import SQLAlchemy
3438

3539
# Create Flask application
3640
app = Flask(__name__)
3741

42+
# We'll just use SQLite here so we don't need an external database
43+
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db/development.db'
44+
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
45+
app.config['SECRET_KEY'] = 'please, tell nobody... Shhhh'
46+
app.config['LOGGING_LEVEL'] = logging.INFO
47+
48+
# Initialize SQLAlchemy
49+
db = SQLAlchemy(app)
50+
3851
# Pull options from environment
3952
DEBUG = (os.getenv('DEBUG', 'False') == 'True')
4053
PORT = os.getenv('PORT', '5000')
4154

55+
class DataValidationError(Exception):
56+
""" Used for an data validation errors when deserializing """
57+
pass
58+
4259
######################################################################
4360
# Error Handlers
4461
######################################################################
@@ -83,6 +100,136 @@ def internal_server_error(error):
83100
return jsonify(status=500, error='Internal Server Error', message=message), 500
84101

85102

103+
#########################################################################
104+
class Pet(db.Model):
105+
"""
106+
Class that represents a Pet
107+
108+
This version uses arelational database for persistence
109+
"""
110+
logger = logging.getLogger(__name__)
111+
112+
# Table Schema
113+
id = db.Column(db.Integer, primary_key=True)
114+
name = db.Column(db.String(63))
115+
category = db.Column(db.String(63))
116+
available = db.Column(db.Boolean())
117+
118+
# def __init__(self, id=0, name='', category=''):
119+
# """ Initialize a Pet """
120+
# self.id = id
121+
# self.name = name
122+
# self.category = category
123+
124+
125+
def __repr__(self):
126+
return '<Pet %r>' % (self.name)
127+
128+
def save(self):
129+
"""
130+
Saves a Pet to the data store
131+
"""
132+
if not self.id:
133+
db.session.add(self)
134+
db.session.commit()
135+
136+
def delete(self):
137+
""" Removes a Pet from the data store """
138+
db.session.delete(self)
139+
db.session.commit()
140+
141+
def serialize(self):
142+
""" Serializes a Pet into a dictionary """
143+
return {"id": self.id,
144+
"name": self.name,
145+
"category": self.category,
146+
"available": self.available}
147+
148+
def deserialize(self, data):
149+
"""
150+
Deserializes a Pet from a dictionary
151+
152+
Args:
153+
data (dict): A dictionary containing the Pet data
154+
"""
155+
if not isinstance(data, dict):
156+
raise DataValidationError('Invalid pet: body of request contained bad or no data')
157+
try:
158+
self.name = data['name']
159+
self.category = data['category']
160+
self.available = data['available']
161+
except KeyError as error:
162+
raise DataValidationError('Invalid pet: missing ' + error.args[0])
163+
except TypeError as error:
164+
raise DataValidationError('Invalid pet: body of request contained' \
165+
'bad or no data')
166+
return self
167+
168+
@staticmethod
169+
def init_db():
170+
""" Initializes the database session """
171+
Pet.logger.info('Initializing database')
172+
db.create_all() # make our sqlalchemy tables
173+
174+
@staticmethod
175+
def all():
176+
""" Returns all of the Pets in the database """
177+
Pet.logger.info('Processing all Pets')
178+
return Pet.query.all()
179+
180+
@staticmethod
181+
def find(pet_id):
182+
""" Finds a Pet by it's ID """
183+
Pet.logger.info('Processing lookup for id %s ...', pet_id)
184+
return Pet.query.get(pet_id)
185+
186+
@staticmethod
187+
def find_or_404(pet_id):
188+
""" Find a Pet by it's id """
189+
Pet.logger.info('Processing lookup or 404 for id %s ...', pet_id)
190+
return Pet.query.get_or_404(pet_id)
191+
192+
@staticmethod
193+
def find_by_name(name):
194+
""" Returns all Pets with the given name
195+
196+
Args:
197+
name (string): the name of the Pets you want to match
198+
"""
199+
Pet.logger.info('Processing name query for %s ...', name)
200+
return Pet.query.filter(Pet.name == name)
201+
202+
@staticmethod
203+
def find_by_category(category):
204+
""" Returns all of the Pets in a category
205+
206+
Args:
207+
category (string): the category of the Pets you want to match
208+
"""
209+
Pet.logger.info('Processing category query for %s ...', category)
210+
return Pet.query.filter(Pet.category == category)
211+
212+
@staticmethod
213+
def find_by_availability(available=True):
214+
""" Query that finds Pets by their availability """
215+
""" Returns all Pets by their availability
216+
217+
Args:
218+
available (boolean): True for pets that are available
219+
"""
220+
Pet.logger.info('Processing available query for %s ...', available)
221+
return Pet.query.filter(Pet.available == available)
222+
223+
#########################################################################
224+
225+
226+
227+
228+
229+
230+
231+
232+
86233
######################################################################
87234
# GET INDEX
88235
######################################################################
@@ -190,6 +337,10 @@ def delete_pets(pet_id):
190337
# U T I L I T Y F U N C T I O N S
191338
######################################################################
192339

340+
def init_db():
341+
""" Initialies the SQLAlchemy app """
342+
Pet.init_db()
343+
193344
def check_content_type(content_type):
194345
""" Checks that the media type is correct """
195346
if request.headers['Content-Type'] == content_type:
@@ -222,6 +373,9 @@ def initialize_logging(log_level=logging.INFO):
222373
# M A I N
223374
######################################################################
224375
if __name__ == "__main__":
225-
print "Pet Service Starting..."
376+
print "========================================="
377+
print " P E T S E R V I C E S T A R T I N G"
378+
print "========================================="
226379
initialize_logging(logging.INFO)
380+
db.create_all() # make our sqlalchemy tables
227381
app.run(host='0.0.0.0', port=int(PORT), debug=DEBUG)

0 commit comments

Comments
 (0)