4
\$\begingroup\$

I have a very standard Flask website being built, more or less straight out of the documentation though am now at a point where I'm going a bit off the book and am probably being highly inefficient about how my code handles user-input changes to their account details.

My users will have access to an account_details.html page where they can view and update their profile information.

The account_details.html page displays the Keys from User.user_variables() along with their present values and has a form where users can input the details they wish to update with and then submit changes (see attached image).

The account_details view then, presently, does no validation checks whatsoever and updates the table. I was going to implement checks but then figured I am probably doing things wrongly since I'd begin hard-coding values like minimum char lengths etc.

I suspect I could be doing just about everything more efficiently.

Please can anyone advise what the best way to allow users to update their account details? Happy for all suggestions but can't seem to find much by googling.

Thanks!

Paul.

Here are my bare bones for User-class/form/view/page.

User class

class User(UserMixin, db.Model):
    """
    User model inheriting db.Model for use in the database
    Note: user_type is default to customer, likely to change as site evolves
    """
    id = db.Column(db.Integer, primary_key=True)
    name_first = db.Column(db.String(3), nullable=False)
    name_last = db.Column(db.String(3), nullable=False)
    name_company = db.Column(db.String(3), nullable=False)
    email = db.Column(db.String(3), nullable=False, unique=True)
    tel_office = db.Column(db.String(11), nullable=False)
    tel_mob = db.Column(db.String(11), nullable=False)
    password = db.Column(db.String(8), nullable=False)
    date_created_utc = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
    user_type = db.Column(db.String(), nullable=False, default='customer')

    def user_variables(self):
        """
        :return: List of all user variables which are then used in account_details.html
        the Keys are what users see on the page, the list[0] is what it's called in this Class
        and the list[1] is grabbing the data from current_user
        """
        user_details = {
            'User ID': ['id', current_user.id],
            'First Name': ['name_first', current_user.name_first],
            'Last Name': ['name_last', current_user.name_last],
            'Company': ['name_company', current_user.name_company],
            'Email': ['email', current_user.email],
            'Office Tel': ['tel_office', current_user.tel_office],
            'Mobile Tel': ['tel_mob', current_user.tel_mob],
            'Date Registered': ['date_created_utc', datetime.strftime(current_user.date_created_utc, '%d/%m/%Y')],
            'Account Type': ['user_type', current_user.user_type]
        }
        return user_details

User registration form

class RegistrationForm(FlaskForm):
    """
    Registration form used in Register page
    """
    name_first = StringField(label='First Name', validators=[DataRequired(message='blah')])
    name_last = StringField(label='Last Name', validators=[DataRequired(message='blah')])
    name_company = StringField(label='Company Name', validators=[DataRequired(message='blah')])
    email = StringField(label='Email', validators=[Email(message='blah'), DataRequired("blah")])
    tel_office = StringField(label='Tel: office', validators=[DataRequired(message='blah')])
    tel_mob = StringField(label='Tel: mob', validators=[DataRequired(message='blah')])
    password = PasswordField(label='Password', validators=[DataRequired(message='blah')])
    password_confirm = PasswordField(label='Confirm password', validators=[EqualTo('password', message='blah')])
    submit = SubmitField(label='Register')

account_details view

@app.route('/account_details', methods=['GET', 'POST'])
def account_details():
    if current_user.is_authenticated:
        user_details = current_user.user_variables()
        user = User.query.filter_by(id=current_user.id).first()
        if request.method == 'POST':
            updated_values_dict = request.form.to_dict()
            for k, v in updated_values_dict.items():
                # TODO validation checks
                # TODO password change
                # The 'name' paramater in each form-control is jinja template of
                # update_{{user_details[key][0]}} hence the check for k == 'update_' etc.
                if k == 'update_name_first':
                    user.name_first = v.rstrip()
                if k == 'update_name_last':
                    user.name_last = v.rstrip()
                if k == 'update_name_company':
                    user.name_company = v.rstrip()
                if k == 'update_email':
                    user.email = v.rstrip()
                if k == 'update_tel_office':
                    user.tel_office = v.rstrip()
                if k == 'update_tel_mob':
                    user.tel_mob = v.rstrip()
            db.session.commit()
            return redirect(url_for('account_details'))
    return render_template('account_details.html', user_details=user_details)

account_details.html page

{% extends 'navbar.html' %}

{% block content %}

<div class="container-sm">
    <div class="home-header">
        {% with messages=get_flashed_messages(with_categories=true) %}
        {% if messages %}
        {% for category, message in messages %}
        <div class="alert alert-{{category}}" role="alert">
            {{ message }}
        </div>
        {% endfor %}
        {% endif %}
        {% endwith %}

        <form method="POST">
            <div class="card text-left">
                <div class="card-body">
                    <table class="table table-striped table-dark shadow-lg">
                        {% for key in user_details.keys() %}
                        <tr>
                            <td>{{ key }}</td>
                            <td>{{ user_details[key][1] }}</td>
                            {% if key not in ['User ID', 'Date Registered', 'Account Type'] %}
                            <td>
                                <div class="form-group">
                                    <input class="form-control" id="update_field" name="update_{{user_details[key][0]}}"
                                           placeholder="Type to update {{ key }}">
                                </div>
                            </td>
                            {% else %}
                            <td></td>
                            {% endif %}
                        </tr>
                        {% endfor %}
                    </table>
                </div>
                <button type="submit" class="btn btn-primary btn-sm">Make Changes</button>
        </form>
    </div>
</div>
</div>
{% endblock %}

Image of the account_details.html page

\$\endgroup\$
5
\$\begingroup\$

At the least, this can be done more efficiently:

            if k == 'update_name_first':
                user.name_first = v.rstrip()
            if k == 'update_name_last':
                user.name_last = v.rstrip()
            if k == 'update_name_company':
                user.name_company = v.rstrip()
            if k == 'update_email':
                user.email = v.rstrip()
            if k == 'update_tel_office':
                user.tel_office = v.rstrip()
            if k == 'update_tel_mob':
                user.tel_mob = v.rstrip()

can be

setattr(
    user,
    k.split('update_', 1)[-1],
    v.rstrip(),
)

depending on how much validation you need to do.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, I will certainly look into setattr() \$\endgroup\$ – Paul Wilson Oct 15 '20 at 0:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.