Skip to content

Commit 8739daf

Browse files
committed
Fixed model relationships, added querying by model.
1 parent dd873a0 commit 8739daf

File tree

8 files changed

+240
-48
lines changed

8 files changed

+240
-48
lines changed

database/connect.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66

77
# Create database engine
88
db = create_engine(
9-
SQLALCHEMY_DATABASE_URI,
10-
connect_args={"ssl": {"key": SQLALCHEMY_DATABASE_PEM}},
11-
echo=True,
9+
SQLALCHEMY_DATABASE_URI, connect_args={"ssl": {"key": SQLALCHEMY_DATABASE_PEM}}
1210
)
1311

1412
# Create database session

sqlalchemy_tutorial/__init__.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .logger import LOGGER
55
from .part1_connections import fetch_job_listings, update_job_listing
66
from .part2_orm_models import orm_create_user, orm_delete_user
7+
from .part3_relationships import get_posts, orm_create_data
78

89

910
def init_script():
@@ -16,5 +17,10 @@ def init_script():
1617

1718
# Part 2: Implementing an ORM
1819
LOGGER.info("Part 2: Create and delete records from a data model.")
19-
orm_create_user(session)
20-
orm_delete_user(session)
20+
user = orm_create_user(session)
21+
orm_delete_user(session, user)
22+
23+
# Part 3: ORM relationships
24+
LOGGER.info("Part 3: Utilize relationships to execute JOINs.")
25+
orm_create_data(session)
26+
get_posts(session)

sqlalchemy_tutorial/part2_orm_models/models.py

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class User(Base):
2121
last_name = Column(String(255))
2222
bio = Column(Text)
2323
avatar_url = Column(Text)
24+
role = Column(String(255))
2425
last_seen = Column(DateTime)
2526
created_at = Column(DateTime, server_default=func.now())
2627
updated_at = Column(DateTime, onupdate=func.now())
+31-23
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
"""Create, delete and update records with SQLAlchemy's ORM."""
2+
from typing import Optional
3+
4+
from sqlalchemy.exc import IntegrityError
25
from sqlalchemy.orm import Session
36

47
from sqlalchemy_tutorial.logger import LOGGER
58
from sqlalchemy_tutorial.part2_orm_models.models import User
69

7-
# Define instance of our `User` model
8-
user = User(
9-
username="admin",
10-
password="Please don't set passwords like this",
11-
12-
first_name="Todd",
13-
last_name="Birchard",
14-
bio="I write tutorials on the internet.",
15-
avatar_url="https://storage.googleapis.com/hackersandslackers-cdn/authors/[email protected]",
16-
)
17-
1810

19-
def orm_create_user(session: Session):
11+
def orm_create_user(session: Session) -> Optional[User]:
2012
"""
2113
Create a new instance of our `User` model.
2214
@@ -25,22 +17,38 @@ def orm_create_user(session: Session):
2517
2618
:returns: None
2719
"""
28-
session.add(user) # Add the user
29-
session.commit() # Commit the change
30-
31-
LOGGER.info(f"Create new user: {user}")
32-
33-
34-
def orm_delete_user(session: Session):
20+
try:
21+
user = User(
22+
username="admin",
23+
password="Please don't set passwords like this",
24+
25+
first_name="Todd",
26+
last_name="Birchard",
27+
bio="I write tutorials on the internet.",
28+
avatar_url="https://storage.googleapis.com/hackersandslackers-cdn/authors/[email protected]",
29+
)
30+
session.add(user) # Add the user
31+
session.commit() # Commit the change
32+
LOGGER.info(f"Created new user: {user}")
33+
return user
34+
except IntegrityError as e:
35+
LOGGER.error(e.orig)
36+
37+
38+
def orm_delete_user(session: Session, user: User):
3539
"""
3640
Delete a user if it exists.
3741
3842
:param session: SQLAlchemy database session.
3943
:type session: Session
44+
:param user: User created in the above function.
45+
:type user: User
4046
4147
:returns: None
4248
"""
43-
session.delete(user) # Delete the user
44-
session.commit() # Commit the change
45-
46-
LOGGER.info(f"Deleted existing user: {user}")
49+
try:
50+
session.delete(user) # Delete the user
51+
session.commit() # Commit the change
52+
LOGGER.info(f"Deleted existing user: {user}")
53+
except IntegrityError as e:
54+
LOGGER.error(e.orig)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .joins import get_posts
2+
from .orm import orm_create_data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Perform JOIN queries on models with relationships."""
2+
from sqlalchemy.orm import Session
3+
4+
from sqlalchemy_tutorial.logger import LOGGER
5+
from sqlalchemy_tutorial.part3_relationships.models import Comment, Post, User
6+
7+
8+
def get_posts(session: Session):
9+
"""
10+
Fetch posts.
11+
12+
:returns: None
13+
"""
14+
posts = session.query(Post).all()
15+
for post in posts:
16+
LOGGER.info(f"Post: {post}")
17+
LOGGER.info(f"Post author: {post.author}")

sqlalchemy_tutorial/part3_relationships/models.py

+46-20
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,69 @@
99
Base = declarative_base()
1010

1111

12-
class Player(Base):
13-
"""Individual player belonging to a team."""
12+
class User(Base):
13+
"""User account."""
1414

15-
__tablename__ = "player"
15+
__tablename__ = "user"
1616

1717
id = Column(Integer, primary_key=True, autoincrement="auto")
18-
team_id = Column(Integer, ForeignKey("team.id"), nullable=False)
19-
first_name = Column(String(255), nullable=False)
20-
last_name = Column(String(255), nullable=False)
21-
position = Column(String(100), nullable=False)
22-
injured = Column(Boolean)
23-
description = Column(Text)
18+
username = Column(String(255), unique=True, nullable=False)
19+
password = Column(Text, nullable=False)
20+
email = Column(String(255), unique=True, nullable=False)
21+
first_name = Column(String(255))
22+
last_name = Column(String(255))
23+
bio = Column(Text)
24+
avatar_url = Column(Text)
25+
role = Column(String(255))
26+
last_seen = Column(DateTime)
2427
created_at = Column(DateTime, server_default=func.now())
2528
updated_at = Column(DateTime, onupdate=func.now())
2629

30+
def __repr__(self):
31+
return "<User %r>" % self.username
32+
33+
34+
class Comment(Base):
35+
"""User-generated comment on a blog post."""
36+
37+
__tablename__ = "comment"
38+
39+
id = Column(Integer, primary_key=True, index=True)
40+
user_id = Column(Integer, ForeignKey("user.id"))
41+
post_id = Column(Integer, index=True)
42+
body = Column(Text)
43+
upvotes = Column(Integer, default=1)
44+
removed = Column(Boolean, default=False)
45+
created_at = Column(DateTime, server_default=func.now())
46+
2747
# Relationships
28-
team = relationship("Team")
48+
user = relationship("User", backref="comments")
2949

3050
def __repr__(self):
31-
return "<Player {}>".format(self.id)
51+
return "<Comment %r>" % self.id
3252

3353

34-
class Team(Base):
35-
"""Team consisting of many players."""
54+
class Post(Base):
55+
"""Blog post/article."""
3656

37-
__tablename__ = "team"
57+
__tablename__ = "post"
3858

39-
id = Column(Integer, primary_key=True, autoincrement="auto")
40-
name = Column(String(255), nullable=False)
41-
city = Column(String(255), nullable=False)
42-
abbreviation = Column(String(255), nullable=False)
43-
logo = Column(String(255), nullable=False)
59+
id = Column(Integer, primary_key=True, index=True)
60+
author_id = Column(Integer, ForeignKey("user.id"))
61+
slug = Column(String(255), nullable=False, unique=True)
62+
title = Column(String(255), nullable=False)
63+
summary = Column(String(400))
64+
feature_image = Column(String(300))
65+
body = Column(Text)
66+
status = Column(String(255), nullable=False, default="unpublished")
4467
created_at = Column(DateTime, server_default=func.now())
4568
updated_at = Column(DateTime, onupdate=func.now())
4669

70+
# Relationships
71+
author = relationship("User", backref="posts")
72+
4773
def __repr__(self):
48-
return "<Team {}>".format(self.id)
74+
return "<Post %r>" % self.slug
4975

5076

5177
Base.metadata.create_all(db)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""Create records from our data models with SQLAlchemy's ORM."""
2+
from typing import Tuple
3+
4+
from sqlalchemy.exc import IntegrityError
5+
from sqlalchemy.orm import Session
6+
7+
from sqlalchemy_tutorial.logger import LOGGER
8+
from sqlalchemy_tutorial.part3_relationships.models import Comment, Post, User
9+
10+
11+
def orm_create_data(session: Session) -> None:
12+
"""
13+
Populate our database with users, posts, and comments to query on.
14+
15+
:param session: SQLAlchemy database session.
16+
:type session: Session
17+
18+
:returns: None
19+
"""
20+
create_users(session)
21+
create_post(session)
22+
create_comment(session)
23+
LOGGER.info(f"Finished creating user, post, and comment records.")
24+
25+
26+
def create_users(session: Session) -> Tuple[User, User]:
27+
"""
28+
Create a couple of user accounts.
29+
30+
:param session: SQLAlchemy database session.
31+
:type session: Session
32+
33+
:returns: Tuple[User, User]
34+
"""
35+
admin_user = User(
36+
username="toddthebod",
37+
password="Please don't set passwords like this",
38+
39+
first_name="Todd",
40+
last_name="Birchard",
41+
bio="I write tutorials on the internet.",
42+
avatar_url="https://storage.googleapis.com/hackersandslackers-cdn/authors/[email protected]",
43+
role="admin",
44+
)
45+
regular_user = User(
46+
username="obnoxioustroll69",
47+
password="Please don't set passwords like this",
48+
49+
first_name="Chad",
50+
last_name="Bowswick",
51+
bio="I leave hurtful comments on coding tutorials I find on the internet.",
52+
avatar_url="https://storage.googleapis.com/hackersandslackers-cdn/authors/[email protected]",
53+
)
54+
admin_user = create_new_user(admin_user, session)
55+
regular_user = create_new_user(regular_user, session)
56+
LOGGER.success(f"Created 2 users: {admin_user} & {regular_user}")
57+
return admin_user, regular_user
58+
59+
60+
def create_new_user(user: User, session: Session) -> User:
61+
"""
62+
Create a new user if username isn't already taken.
63+
64+
:param user: New user record to create.
65+
:type user: User
66+
:param session: SQLAlchemy database session.
67+
:type session: Session
68+
:return: Optional[User]
69+
"""
70+
try:
71+
user_query = session.query(User).filter(User.username == user.username).first()
72+
if user_query is None:
73+
session.add(user) # Add the user
74+
session.commit() # Commit the change
75+
LOGGER.success(f"Created user: {user}")
76+
LOGGER.warning(f"User already exists in database: {user}")
77+
return user
78+
except IntegrityError as e:
79+
LOGGER.error(e.orig)
80+
81+
82+
def create_post(session: Session) -> Post:
83+
"""
84+
Create a post authored by `admin_user`.
85+
86+
:param session: SQLAlchemy database session.
87+
:type session: Session
88+
89+
:returns: Post
90+
"""
91+
try:
92+
admin_user = session.query(User).filter(User.username == "toddthebod").first()
93+
post = Post(
94+
author_id=admin_user.id,
95+
slug="fake-post-slug",
96+
title="Fake Post Title",
97+
summary="A fake post to have some fake comments.",
98+
feature_image="https://hackersandslackers-cdn.storage.googleapis.com/2021/01/[email protected]",
99+
body="Cheese slices monterey jack cauliflower cheese dolcelatte cheese and wine fromage frais rubber cheese gouda. Rubber cheese cheese and wine cheeseburger cheesy grin paneer paneer taleggio caerphilly. Edam mozzarella.",
100+
)
101+
session.add(admin_user) # Add the user
102+
session.commit() # Commit the change
103+
LOGGER.success(f"Created post {post} published by user {admin_user}")
104+
return post
105+
except IntegrityError as e:
106+
LOGGER.error(e.orig)
107+
108+
109+
def create_comment(session: Session) -> Comment:
110+
"""
111+
Create a comment posted by `regular_user` on `admin_user`'s post.
112+
113+
:param session: SQLAlchemy database session.
114+
:type session: Session
115+
116+
:returns: Comment
117+
"""
118+
try:
119+
regular_user = (
120+
session.query(User).filter(User.username == "obnoxioustroll69").first()
121+
)
122+
post = session.query(Post).filter(Post.id == 1).first()
123+
comment = Comment(
124+
user_id=regular_user.id,
125+
post_id=post.id,
126+
body="This post about SQLAlchemy is awful. You didn't even bother to explain how to install Python, which is where I (and so many others) got stuck. Plus, your code doesn't even work!! I cloned your code and it keeps giving me `environment variable` errors... WTF are environment variables?!!?!?",
127+
upvotes=2,
128+
)
129+
session.add(comment) # Add the Comment
130+
session.commit() # Commit the change
131+
LOGGER.success(f"Created comment {comment} posted by user {regular_user}")
132+
return comment
133+
except IntegrityError as e:
134+
LOGGER.error(e.orig)

0 commit comments

Comments
 (0)