I am working on a simple CRUD app as a personal project using Flask. I am currently working on the user blueprint and trying to use as less as libraries as I could and no ORM (for learning purposes). On my blueprints, I have multiple folders including a user
folder. I wrote 2 classes in blueprints/user/models.py
:
User
: represent a User entity
UserService
: register and login (login is not present yet)
Does it make sense to have2 separate classes? I am actually thinking to move all of my User methods to the
UserService
class and just have aUserService
class with setters and getters.I am confused about is where and when to interact with the database. Should I do it directly on my
route.py
vs create a new class?
For instance, to add a new User
to the database, my SQL should look more or less like this:
sql = "INSERT INTO users (email, password) VALUES (%s, %s)"
cursor = conn.cursor()
cursor.execute(sql, (email, password))
cursor.fetchall()
conn.commit()
blueprints/user/models.py
class UserService():
def register_user(self,
email,
password,
registration_date,
active,
sign_in_count,
current_sign_in_on,
last_sign_in_on):
new_user = User(email, password, registration_date, active, sign_in_count, current_sign_in_on, last_sign_in_on)
return new_user.__str__()
class User():
def __init__(self, email, password, registration_date, active, sign_in_count, current_sign_in_on, last_sign_in_on ):
self.email = email
self.password = password
self.registration_date = registration_date
self.active = active
# Activity tracking
self.sign_in_count = sign_in_count
self.current_sign_in_on = current_sign_in_on
self.last_sign_in_on = last_sign_in_on
def desactivate_user(self):
if self.active == False:
print(f"User {self.email} is already inactive")
self.active = False
def reactive_user(self):
if self.active == True:
print(f"User {self.email} is already active")
self.active = True
def is_active(self):
return self.active
def update_activity_tracking(self, ip_address):
self.sign_in_count += 1
self.last_sign_in_on = self.current_sign_in_on
self.current_sign_in_on = datetime.datetime.now()
def update_password(self, new_password):
self.password = get_hashed_password(new_password)
def __str__(self):
user_attributes = vars(self)
return (', '.join("%s: %s" % item for item in user_attributes.items()))
blueprints/user/views.py
from flask import Blueprint, render_template, request, jsonify
from prepsmarter.extensions import conn #database connection variable
user = Blueprint('user', __name__, template_folder='templates')
@user.route('/register')
def login():
return render_template('register.html')
@user.route('/new-user',methods = ['POST'])
def register_user():
# where I am going to register a new user
extensions/Database.py
import pymysql
class Database:
instance = None
def __init__(self, host, user, password, db):
self.host = host
self.user = user
self. password = password
self.db = db
def connect(self):
try:
conn = pymysql.connect(
host = self.host,
user = self.user,
passwd = self.password,
db = self.db
)
return conn
except Exception as e:
print(e)
-
\$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers . Please consider asking a new question instead. Feel free to add links in both questions towards another. \$\endgroup\$Mast– Mast ♦2021年02月28日 14:53:15 +00:00Commented Feb 28, 2021 at 14:53
1 Answer 1
In general, your code seems to be in pretty good shape if you're hoping to move towards domain driven design, notably seen from your "entity-like" User object and the UserService.
Does it make sense to have2 separate classes? I am actually thinking to move all of my User methods to the UserService class and just have a UserService class with setters and getters.
First off, if you're anticipating your application to grow, a clear separation of concerns is what you'd want to achieve and to further achieve this, the two objects you have are fine. That is, one to represent a definition of what a user is and another to determine what you can do with a user object or related objects which in this case is the UserService
. This naturally means that the methods on the User
class should now be part of the UserService
, considering them business logic of the application. I would use the words model to describe the User
class and service to describe the UserService
to define the types of components for the application.
I am confused about is where and when to interact with the database. Should I do it directly on my route.py vs create a new class?
One thing that you can also consider from domain driven design is also to include a repository object when your application becomes large enough. Repositories typically carry operations that might interact with a database, replacing what may be seen like an ORM that you have mentioned. Otherwise, if you're still small and nimble and don't have too many complex relationships in your database, you can keep it simple and keep the database queries in your model for simplicity for the time being. Both allows the user to be created from anywhere if needed and enables code re-use more than including them in the route. Designing from an interface perspective consider the following.
user_repository = UserRepository(db_conn)
user_service = UserService()
foo_user = user_service.register_user(...)
// using the repository
repository.save(foo_user)
// using the model
foo_user.save()
I'll end this with, it depends how large your application is growing, the larger it is, the more you want to re-evaluate your design so to better organize groups of functionality to better extend from and if possible re-use code. That said, what you may have could be good in the long run if your application doesn't really grow beyond those needs.
-
\$\begingroup\$ Thank you Will, this is a really great answer! I just moved all of my methods from the
User
to theUserService
class. Since all of those methods require aUser
object, do I need to pass aUser
object in theUserService
constructor or just have aUser
argument for eachUserService
methods? \$\endgroup\$Pierre-Alexandre– Pierre-Alexandre2021年02月28日 10:51:39 +00:00Commented Feb 28, 2021 at 10:51 -
\$\begingroup\$ I just figured out that in the constructor won't be possible since I do have a
register_user
method in theUserService
class now and the goal of this method is actually to create a user. \$\endgroup\$Pierre-Alexandre– Pierre-Alexandre2021年02月28日 11:00:55 +00:00Commented Feb 28, 2021 at 11:00 -
\$\begingroup\$ I just updatd my post with the updated and new code :) \$\endgroup\$Pierre-Alexandre– Pierre-Alexandre2021年02月28日 11:09:45 +00:00Commented Feb 28, 2021 at 11:09
-
\$\begingroup\$ @Pierre-Alexandre Generally, the
UserService
is on the business logic layer which generally means, it won't take a singleuser
object as the constructor of theUserService
. Rather, all the methods on theUserService
instance probably takes a user object or creates a user object, for cases like updating, reference to be used in conjunction for other business logic. Let me know if that helps. \$\endgroup\$Will C– Will C2021年03月01日 04:14:28 +00:00Commented Mar 1, 2021 at 4:14 -
\$\begingroup\$ Yes, I did follow your suggestions and it's working \$\endgroup\$Pierre-Alexandre– Pierre-Alexandre2021年03月04日 18:54:16 +00:00Commented Mar 4, 2021 at 18:54