#StackBounty: #python #object-oriented #design-patterns #flask Python: separation of concern User Model and User DB

Bounty: 150

I am working on a small side-project since a couple of weeks, I am using Flask but trying to use as few libraries as possible and no ORMs (for learning purposes.

I am currently working on the User service on my application and currently stuck in the registration email confirmation part regarding my design.

Since I am not using any ORM, I wrote those classes:

  • UserRepository: interact with the user table in the database (add, get, delete user and save)
  • UserService: actions on a User (registration, deactivate user, update stats)
  • User: representation of a User entity, no methods, methods are in the UserService (most of them take a User as a parameter expect the one to create a new User)

Current issue:

When a new user register, I send a registration email with a URL link to activate the account. I am using URLSafeTimedSerializer to do that and dump the user.email to generate the account confirmation URL.
The email is sent and when the user click on the link, I am able to decrypt the user.email using URLSafeTimedSerializer.loads(). But with my current design, I am not quite confident how I am going to retrieve the user.email. Maybe the UserService class should take the UserRepository as an argument?

user/views.py

user = Blueprint('user', __name__, template_folder='templates')

# ----------
# STUCK HERE 
# ----------
@user.route('/activate/<activation_token>', methods=["GET"])
def activate(activation_token):
    private_key = current_app.config['SECRET_KEY']
    s = URLSafeTimedSerializer(private_key)
    user_email = s.loads(activation_token)
    # now I need to check if that user is already confirmed, if not I have to change current User confirmed value from from False to True
    # I should probably create a new UserService instance and create a method get_user_by_email(email) ?

   def generate_registration_token(email):
    private_key = current_app.config['SECRET_KEY']
    s = URLSafeTimedSerializer(private_key)
    u = s.dumps(email)
    return u

def send_registration_email(email):
    from prepsmarter.blueprints.user.tasks import deliver_contact_email
    token = generate_registration_token(email)
    host = current_app.config['HOST']
    activation_url = f"{host}/activate/{token}"
    try:
        deliver_contact_email(email, activation_url)
        return "ok"
    except Exception as e:
        return str(e)
    
@user.route('/register')
def login():
    return render_template('register.html')

@user.route('/new-user',methods = ['POST'])
def register_user():
    form_email = request.form.get('email')
    form_password = request.form.get('psw')
    form_password_repeat = request.form.get('psw-repeat')
    registration_form = RegistrationForm(form_email, form_password, form_password_repeat).validate_registration()
    if registration_form:
        new_user = UserService().register_user(form_email, form_password)
        user_repository = UserRepository(conn, 'users')
        user_repository.add_user(new_user)
        user_repository.save()
        send_registration_email(new_user.email)
        return "new user created" #will probably change return statements later on
    return "new uer not created" #will probably change return statements later on

user/models.py (User class)

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
        self.confirmed = False

user/services.py (UserRepository)

class UserRepository():
    def __init__(self, conn, table):
        self.conn = conn
        self.table = table  
    
    #select exists(select 1 from users where email='pamousset75@gmail.com')
    def add_user(self, user):
        sql = "INSERT INTO users (email, password, is_active, sign_in_count, current_sign_in_on, last_sign_in_on) VALUES (%s, %s, %s, %s, %s, %s)"
        cursor = self.conn.cursor()
        # the is_active column in the DB is a tinyint(1). True = 1 and False = 0
        if user.active == True:
            is_active = 1
        is_active = 0
        cursor.execute(sql, ( user.email, user.password, is_active, user.sign_in_count, user.current_sign_in_on, user.last_sign_in_on))
        resp = cursor.fetchall()
        return resp
    
    def delete_user(self):
        return ""
    
    def get_user(self):
        return ""
    
    def save(self):
        self.conn.commit()
    

user/services.py (UserService)

class UserService():

def register_user(self,
                  email,
                  password):
    sign_in_count = 1
    today_date = datetime.today().strftime('%Y-%m-%d')
    active = True
    new_user = User(email, password, today_date, active,
                    sign_in_count, today_date, today_date)
    return new_user        

def desactivate_user(self, User):
    if User.active == False:
        print(f"User {User.email} is already inactive")
    User.active = False

def reactive_user(self, User):
    if User.active == True:
        print(f"User {User.email} is already active")
    User.active = True

def is_active(self, User):
    return User.is_active


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.