Skip to content

Commit 4ec9976

Browse files
authored
Merge branch 'master' into flush-print
2 parents f2948ad + 1054489 commit 4ec9976

File tree

164 files changed

+19605
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+19605
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.db
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"printWidth": 79
3+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Build a Front End for a REST API
2+
3+
This repository contains code related to the Real Python tutorial on [building a front end for a REST API](https://realpython.com/build-a-rest-api-frontend/).
4+
5+
## Setup
6+
7+
You should first create a virtual environment:
8+
9+
```console
10+
$ python -m venv venv
11+
$ source venv/bin/activate
12+
```
13+
14+
Install the pinned dependencies from `requirements.txt`:
15+
16+
```console
17+
(venv) $ python -m pip install -r requirements.txt
18+
```
19+
20+
Then you can navigate into the folder `source_code_start/` and create a new database:
21+
22+
```sh
23+
(venv) $ cd source_code_start
24+
(venv) $ python init_database.py
25+
```
26+
27+
This will create or update the `people.db` database that you can use with your project.
28+
29+
After building the database, you can start the Flask server:
30+
31+
```sh
32+
(venv) $ python app.py
33+
```
34+
35+
When the Flask server is running, you can visit the front end on `http://localhost:8000` and the API documentation on `http://localhost:8000/api/ui`.
36+
37+
## Author
38+
39+
- **Philipp Acsany**, E-mail: [[email protected]]([email protected])
40+
41+
## License
42+
43+
Distributed under the MIT license. See [`LICENSE`](../LICENSE) for more information.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
attrs==22.1.0
2+
certifi==2022.9.24
3+
charset-normalizer==2.1.1
4+
click==8.1.3
5+
clickclick==20.10.2
6+
connexion==2.14.1
7+
Flask==2.2.2
8+
flask-marshmallow==0.14.0
9+
Flask-SQLAlchemy==3.0.2
10+
idna==3.4
11+
inflection==0.5.1
12+
itsdangerous==2.1.2
13+
Jinja2==3.1.2
14+
jsonschema==4.16.0
15+
MarkupSafe==2.1.1
16+
marshmallow==3.18.0
17+
marshmallow-sqlalchemy==0.28.1
18+
packaging==21.3
19+
pyparsing==3.0.9
20+
pyrsistent==0.19.1
21+
PyYAML==6.0
22+
requests==2.28.1
23+
six==1.16.0
24+
SQLAlchemy==1.4.42
25+
swagger-ui-bundle==0.0.9
26+
urllib3==1.26.12
27+
Werkzeug==2.2.2
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from flask import render_template
2+
3+
import config
4+
from models import Person
5+
6+
app = config.connex_app
7+
app.add_api(config.basedir / "swagger.yml")
8+
9+
10+
@app.route("/")
11+
def home():
12+
people = Person.query.all()
13+
return render_template("home.html", people=people)
14+
15+
16+
if __name__ == "__main__":
17+
app.run(host="0.0.0.0", port=8000, debug=True)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import pathlib
2+
3+
import connexion
4+
from flask_marshmallow import Marshmallow
5+
from flask_sqlalchemy import SQLAlchemy
6+
7+
basedir = pathlib.Path(__file__).parent.resolve()
8+
connex_app = connexion.App(__name__, specification_dir=basedir)
9+
10+
app = connex_app.app
11+
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{basedir / 'people.db'}"
12+
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
13+
14+
db = SQLAlchemy(app)
15+
ma = Marshmallow(app)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from datetime import datetime
2+
from sqlalchemy.exc import OperationalError
3+
4+
from config import app, db
5+
from models import Note, Person
6+
7+
PEOPLE_NOTES = [
8+
{
9+
"lname": "Fairy",
10+
"fname": "Tooth",
11+
"notes": [
12+
("I brush my teeth after each meal.", "2022-01-06 17:10:24"),
13+
(
14+
"The other day a friend said, I have big teeth.",
15+
"2022-03-05 22:17:54",
16+
),
17+
("Do you pay per gram?", "2022-03-05 22:18:10"),
18+
],
19+
},
20+
{
21+
"lname": "Ruprecht",
22+
"fname": "Knecht",
23+
"notes": [
24+
(
25+
"I swear, I'll do better this year.",
26+
"2022-01-01 09:15:03",
27+
),
28+
(
29+
"Really! Only good deeds from now on!",
30+
"2022-02-06 13:09:21",
31+
),
32+
],
33+
},
34+
{
35+
"lname": "Bunny",
36+
"fname": "Easter",
37+
"notes": [
38+
(
39+
"Please keep the current inflation rate in mind!",
40+
"2022-01-07 22:47:54",
41+
),
42+
("No need to hide the eggs this time.", "2022-04-06 13:03:17"),
43+
],
44+
},
45+
]
46+
47+
48+
def get_data_from_table(model):
49+
try:
50+
data = db.session.query(model).all()
51+
db.session.close()
52+
return data
53+
except OperationalError:
54+
return []
55+
56+
57+
def create_database(db):
58+
db.create_all()
59+
for data in PEOPLE_NOTES:
60+
new_person = Person(lname=data.get("lname"), fname=data.get("fname"))
61+
for content, timestamp in data.get("notes", []):
62+
new_person.notes.append(
63+
Note(
64+
content=content,
65+
timestamp=datetime.strptime(
66+
timestamp, "%Y-%m-%d %H:%M:%S"
67+
),
68+
)
69+
)
70+
db.session.add(new_person)
71+
db.session.commit()
72+
print("Created new database")
73+
74+
75+
def update_database(db, existing_people, existing_notes):
76+
db.drop_all()
77+
db.create_all()
78+
for person in existing_people:
79+
db.session.merge(person)
80+
for note in existing_notes:
81+
db.session.merge(note)
82+
db.session.commit()
83+
print("Updated existing database")
84+
85+
86+
with app.app_context():
87+
existing_people = get_data_from_table(Person)
88+
existing_notes = get_data_from_table(Note)
89+
90+
if not existing_people:
91+
create_database(db)
92+
else:
93+
update_database(db, existing_people, existing_notes)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from datetime import datetime
2+
3+
from marshmallow_sqlalchemy import fields
4+
5+
from config import db, ma
6+
7+
8+
class Note(db.Model):
9+
__tablename__ = "note"
10+
id = db.Column(db.Integer, primary_key=True)
11+
person_id = db.Column(db.Integer, db.ForeignKey("person.id"))
12+
content = db.Column(db.String, nullable=False)
13+
timestamp = db.Column(
14+
db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
15+
)
16+
17+
18+
class NoteSchema(ma.SQLAlchemyAutoSchema):
19+
class Meta:
20+
model = Note
21+
load_instance = True
22+
sqla_session = db.session
23+
include_fk = True
24+
25+
26+
class Person(db.Model):
27+
__tablename__ = "person"
28+
id = db.Column(db.Integer, primary_key=True)
29+
lname = db.Column(db.String(32), nullable=False)
30+
fname = db.Column(db.String(32))
31+
timestamp = db.Column(
32+
db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
33+
)
34+
35+
notes = db.relationship(
36+
Note,
37+
backref="person",
38+
cascade="all, delete, delete-orphan",
39+
single_parent=True,
40+
order_by="desc(Note.timestamp)",
41+
)
42+
43+
44+
class PersonSchema(ma.SQLAlchemyAutoSchema):
45+
class Meta:
46+
model = Person
47+
load_instance = True
48+
sqla_session = db.session
49+
include_relationships = True
50+
51+
notes = fields.Nested(NoteSchema, many=True)
52+
53+
54+
note_schema = NoteSchema()
55+
person_schema = PersonSchema()
56+
people_schema = PersonSchema(many=True)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from flask import abort, make_response
2+
3+
from config import db
4+
from models import Note, Person, note_schema
5+
6+
7+
def read_one(note_id):
8+
note = Note.query.get(note_id)
9+
10+
if note is not None:
11+
return note_schema.dump(note)
12+
else:
13+
abort(404, f"Note with ID {note_id} not found")
14+
15+
16+
def update(note_id, note):
17+
existing_note = Note.query.get(note_id)
18+
19+
if existing_note:
20+
update_note = note_schema.load(note, session=db.session)
21+
existing_note.content = update_note.content
22+
db.session.merge(existing_note)
23+
db.session.commit()
24+
return note_schema.dump(existing_note), 201
25+
else:
26+
abort(404, f"Note with ID {note_id} not found")
27+
28+
29+
def delete(note_id):
30+
existing_note = Note.query.get(note_id)
31+
32+
if existing_note:
33+
db.session.delete(existing_note)
34+
db.session.commit()
35+
return make_response(f"{note_id} successfully deleted", 204)
36+
else:
37+
abort(404, f"Note with ID {note_id} not found")
38+
39+
40+
def create(note):
41+
person_id = note.get("person_id")
42+
person = Person.query.get(person_id)
43+
44+
if person:
45+
new_note = note_schema.load(note, session=db.session)
46+
person.notes.append(new_note)
47+
db.session.commit()
48+
return note_schema.dump(new_note), 201
49+
else:
50+
abort(404, f"Person not found for ID: {person_id}")
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from flask import abort, make_response
2+
3+
from config import db
4+
from models import Person, people_schema, person_schema
5+
6+
7+
def read_all():
8+
people = Person.query.all()
9+
return people_schema.dump(people)
10+
11+
12+
def create(person):
13+
new_person = person_schema.load(person, session=db.session)
14+
db.session.add(new_person)
15+
db.session.commit()
16+
return person_schema.dump(new_person), 201
17+
18+
19+
def read_one(person_id):
20+
person = Person.query.get(person_id)
21+
22+
if person is not None:
23+
return person_schema.dump(person)
24+
else:
25+
abort(404, f"Person with ID {person_id} not found")
26+
27+
28+
def update(person_id, person):
29+
existing_person = Person.query.get(person_id)
30+
31+
if existing_person:
32+
update_person = person_schema.load(person, session=db.session)
33+
existing_person.fname = update_person.fname
34+
existing_person.lname = update_person.lname
35+
db.session.merge(existing_person)
36+
db.session.commit()
37+
return person_schema.dump(existing_person), 201
38+
else:
39+
abort(404, f"Person with ID {person_id} not found")
40+
41+
42+
def delete(person_id):
43+
existing_person = Person.query.get(person_id)
44+
45+
if existing_person:
46+
db.session.delete(existing_person)
47+
db.session.commit()
48+
return make_response(f"{person_id} successfully deleted", 200)
49+
else:
50+
abort(404, f"Person with ID {person_id} not found")

0 commit comments

Comments
 (0)