Skip to content

Commit f1958c0

Browse files
committed
Updating the code to take care of some recursion problems in Marshmallow, and simplify the database calls (at the expense of getting to much data) to make the article more straightforward.
1 parent 30e655f commit f1958c0

File tree

17 files changed

+705
-470
lines changed

17 files changed

+705
-470
lines changed

flask-connexion-rest-part-3/build_database.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
from datetime import datetime
23
from config import db
34
from models import Person, Note
45

@@ -8,25 +9,25 @@
89
"fname": "Doug",
910
"lname": "Farrell",
1011
"notes": [
11-
"Cool, a mini-blogging application!",
12-
"This could be useful",
13-
"Well, sort of useful"
12+
("Cool, a mini-blogging application!", "2019-01-06 22:17:54"),
13+
("This could be useful", "2019-01-08 22:17:54"),
14+
("Well, sort of useful", "2019-03-06 22:17:54"),
1415
]
1516
},
1617
{
1718
"fname": "Kent",
1819
"lname": "Brockman",
1920
"notes": [
20-
"I'm going to make really profound observations",
21-
"Maybe they'll be more obvious than I thought"
21+
("I'm going to make really profound observations", "2019-01-07 22:17:54"),
22+
("Maybe they'll be more obvious than I thought", "2019-02-06 22:17:54"),
2223
]
2324
},
2425
{
2526
"fname": "Bunny",
2627
"lname": "Easter",
2728
"notes": [
28-
"Has anyone seen my Easter eggs?",
29-
"I'm really late delivering these!"
29+
("Has anyone seen my Easter eggs?", "2019-01-07 22:47:54"),
30+
("I'm really late delivering these!", "2019-04-06 22:17:54"),
3031
]
3132
},
3233
]
@@ -44,7 +45,8 @@
4445

4546
# Add the notes for the person
4647
for note in person.get("notes"):
47-
p.notes.append(Note(content=note))
48+
content, timestamp = note
49+
p.notes.append(Note(content=content, timestamp=datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")))
4850
db.session.add(p)
4951

5052
db.session.commit()

flask-connexion-rest-part-3/models.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ class Person(db.Model):
1313
db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
1414
)
1515
notes = db.relationship('Note',
16-
backref='person',
17-
cascade='all, delete-orphan',
18-
lazy='noload',
16+
backref=db.backref('person',
17+
lazy='joined',
18+
cascade='delete, delete-orphan',
19+
single_parent=True),
1920
order_by='desc(Note.timestamp)')
2021

2122

@@ -33,10 +34,31 @@ class PersonSchema(ma.ModelSchema):
3334
class Meta:
3435
model = Person
3536
sqla_session = db.session
36-
notes = fields.Nested('NoteSchema', default=[], many=True)
37+
notes = fields.Nested('PersonNotesSchema', default=[], many=True)
38+
39+
40+
class PersonNotesSchema(ma.ModelSchema):
41+
"""
42+
This class exists to get around a recursion issue
43+
"""
44+
note_id = fields.Int()
45+
person_id = fields.Int()
46+
content = fields.Str()
47+
timestamp = fields.Str()
3748

3849

3950
class NoteSchema(ma.ModelSchema):
4051
class Meta:
4152
model = Note
4253
sqla_session = db.session
54+
person = fields.Nested('NotesPersonSchema', default=None)
55+
56+
57+
class NotesPersonSchema(ma.ModelSchema):
58+
"""
59+
This class exists to get around a recursion issue
60+
"""
61+
person_id = fields.Int()
62+
lname = fields.Str()
63+
fname = fields.Str()
64+
timestamp = fields.Str()

flask-connexion-rest-part-3/notes.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@
88
from models import Person, Note, NoteSchema
99

1010

11+
def read_all():
12+
"""
13+
This function responds to a request for /api/people/notes
14+
with the complete list of notes, sorted by note timestamp
15+
16+
:return: json list of all notes for all people
17+
"""
18+
# Query the database for all the notes
19+
notes = Note.query.order_by(db.desc(Note.timestamp)).all()
20+
21+
# Serialize the list of notes from our data
22+
note_schema = NoteSchema(many=True, exclude=['person.notes'])
23+
data = note_schema.dump(notes).data
24+
return data
25+
26+
1127
def read_one(person_id, note_id):
1228
"""
1329
This function responds to a request for /api/people/{person_id}/notes/{note_id}

flask-connexion-rest-part-3/people.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from flask import make_response, abort
77
from config import db
88
from models import Person, PersonSchema, Note
9-
from sqlalchemy import desc
109

1110

1211
def read_all():
@@ -25,7 +24,7 @@ def read_all():
2524
return data
2625

2726

28-
def read_one(person_id, get_notes=None):
27+
def read_one(person_id):
2928
"""
3029
This function responds to a request for /api/people/{person_id}
3130
with one matching person from people
@@ -34,16 +33,11 @@ def read_one(person_id, get_notes=None):
3433
:return: person matching id
3534
"""
3635
# Build the initial query
37-
query = Person.query.filter(Person.person_id == person_id)
38-
39-
# Modify the query if we're getting the notes as well
40-
if get_notes:
41-
query = query \
42-
.join(Note) \
43-
.options(db.joinedload(Person.notes))
44-
45-
# Execute the query
46-
person = query.one_or_none()
36+
person = Person.query \
37+
.filter(Person.person_id == person_id) \
38+
.join(Note) \
39+
.options(db.joinedload(Person.notes)) \
40+
.one_or_none()
4741

4842
# Did we find a person?
4943
if person is not None:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
certifi==2018.11.29
2+
chardet==3.0.4
3+
Click==7.0
4+
clickclick==1.2.2
5+
connexion==2.2.0
6+
Flask==1.0.2
7+
flask-marshmallow==0.9.0
8+
Flask-SQLAlchemy==2.3.2
9+
idna==2.8
10+
inflection==0.3.1
11+
itsdangerous==1.1.0
12+
Jinja2==2.10
13+
jsonschema==2.6.0
14+
MarkupSafe==1.1.0
15+
marshmallow==2.17.0
16+
marshmallow-sqlalchemy==0.15.0
17+
openapi-spec-validator==0.2.4
18+
PyYAML==3.13
19+
requests==2.21.0
20+
six==1.12.0
21+
SQLAlchemy==1.2.16
22+
swagger-ui-bundle==0.0.2
23+
urllib3==1.24.1
24+
Werkzeug==0.14.1

flask-connexion-rest-part-3/server.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ def home():
2828
return render_template("home.html")
2929

3030

31+
# Create a URL route in our application for "/people"
32+
@connex_app.route("/people")
33+
def people():
34+
"""
35+
This function just responds to the browser URL
36+
localhost:5000/people
37+
38+
:return: the rendered template "people.html"
39+
"""
40+
return render_template("people.html")
41+
42+
3143
# Create a URL route to the notes page
3244
@connex_app.route("/notes/<int:person_id>")
3345
def notes(person_id):
Lines changed: 0 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +0,0 @@
1-
/*
2-
* This is the CSS stylesheet for the demo people application
3-
*/
4-
5-
@import url(http://fonts.googleapis.com/css?family=Roboto:400,300,500,700);
6-
7-
body, .ui-btn {
8-
font-family: Roboto;
9-
}
10-
11-
.container {
12-
padding: 10px;
13-
}
14-
15-
.banner {
16-
text-align: center;
17-
}
18-
19-
.editor {
20-
width: 50%;
21-
margin-left: auto;
22-
margin-right: auto;
23-
padding: 5px;
24-
border: 1px solid lightgrey;
25-
border-radius: 3px;
26-
margin-bottom: 5px;
27-
}
28-
29-
label {
30-
display: inline-block;
31-
margin-bottom: 5px;
32-
}
33-
34-
button {
35-
padding: 5px;
36-
margin-right: 5px;
37-
border-radius: 3px;
38-
background-color: #eee;
39-
}
40-
41-
.people {
42-
width: 50%;
43-
margin-left: auto;
44-
margin-right: auto;
45-
margin-bottom: 5px;
46-
}
47-
48-
table {
49-
width: 100%;
50-
border-collapse: collapse;
51-
}
52-
53-
table, caption, th, td {
54-
border: 1px solid lightgrey;
55-
}
56-
57-
table caption {
58-
height: 33px;
59-
font-weight: bold;
60-
padding-top: 5px;
61-
border-bottom: none;
62-
}
63-
64-
tr {
65-
cursor: pointer;
66-
height: 33px;
67-
}
68-
69-
tr:nth-child(even) {
70-
background-color: #f0f0f0
71-
}
72-
73-
td {
74-
text-align: center;
75-
}
76-
77-
tbody tr:hover {
78-
background-color: cornsilk;
79-
}
80-
81-
.error {
82-
width: 50%;
83-
margin-left: auto;
84-
margin-right: auto;
85-
padding: 5px;
86-
border: 1px solid lightgrey;
87-
border-radius: 3px;
88-
background-color: #fbb;
89-
visibility: hidden;
90-
}

flask-connexion-rest-part-3/static/css/notes.css

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,6 @@
22
* This is the CSS stylesheet for the notes creation/update/delete application
33
*/
44

5-
@import url(http://fonts.googleapis.com/css?family=Roboto:400,300,500,700);
6-
7-
body, .ui-btn {
8-
font-family: Roboto;
9-
}
10-
11-
.navigation {
12-
border: 2px solid grey;
13-
background-color: lightgrey;
14-
width: 95%;
15-
margin-top: 0px;
16-
margin-left: auto;
17-
margin-right: auto;
18-
margin-bottom: 0px;
19-
padding: 10px;
20-
text-align: left;
21-
}
22-
235
.container {
246
padding: 10px;
257
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This is the parent CSS stylesheet that all pages get basic styling from
3+
*/
4+
5+
@import url(http://fonts.googleapis.com/css?family=Roboto:400,300,500,700);
6+
7+
body, .ui-btn {
8+
font-family: Roboto;
9+
}
10+
11+
.navigation {
12+
display: flex;
13+
flex-direction: row;
14+
padding: 15px 10px 15px 10px;
15+
border-bottom: 2px solid darkgrey;
16+
background-color: whitesmoke;
17+
}
18+
19+
.navigation a {
20+
margin-right: 10px;
21+
padding: 5px;
22+
border: 1px solid darkgrey;
23+
border-radius: 4px;
24+
text-decoration: none;
25+
cursor: pointer;
26+
text: black;
27+
background-color: lightskyblue;
28+
}
29+
30+
.navigation .page_name {
31+
flex-grow: 2;
32+
text-align: center;
33+
font-size: 1.5em;
34+
font-weight: bold;
35+
}
36+
37+
.navigation a:hover {
38+
background-color: deepskyblue;
39+
}
40+
41+
.container {
42+
padding: 10px;
43+
}

0 commit comments

Comments
 (0)