0

I am writing a task management application in Flask and have the following issue.

When a user signs in they see a list of projects and a list of all of their tasks. When they click on a project, I want the view to switch so they only see tasks that belong in that project.

At the moment I have a system that works, but it has some repeated code and I think there must be a better way around this.

This is the view that users see immediately when they log in. There is a list of all their tasks, as well as a list of projects they have created.

The view function for this is home():

@tasks.route('/', methods=['GET', 'POST'])
@login_required
def home():
    user = current_user
    form = NewTaskForm(user=current_user)
    if form.validate_on_submit():
        task = Task(body=form.body.data, due=form.due.data, author=current_user._get_current_object(), project=Project.query.filter_by(id=form.project_id.data).first())
        db.session.add(task)
        db.session.commit()
        flash('Your task has been added!')
    form.body.data = ''
    form.due.data = ''
    tasks = Task.query.filter_by(author_id=current_user.id)
    projects = current_user.projects
    return render_template('tasks/home.html', form=form, tasks=tasks, projects=projects)

The template that renders when this function returns is the following:

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Tasky - Home{% endblock %}

{% block scripts %}
{{ super() }}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
{% endblock %}

{% block page_content %}
<div class="container">
    <div class="page-header">
        <h1>Welcome back {{ current_user.username }}</h1>
    </div>

    <div class="col-md-4">
        <ul class="list-group">
            <li class="list-group-item">
                <a href="{{ url_for('tasks.status', task_status='today') }}">
                    Today
                </a>
            </li>
            <li class="list-group-item"><a href="{{ url_for('tasks.status', task_status='this_week') }}">This Week</a></li>
            <li class="list-group-item"><a href="{{ url_for('tasks.status', task_status='overdue') }}">Overdue</a></li>
        </ul>
        <ul class="list-group">
            {% for project in projects%}
            <li class="list-group-item">
                <a href="{{ url_for('tasks.project', project_id=project.id) }}">
                    {{ project.name }}
                    <span class="badge">
                        {{ project.tasks.count() }}
                    </span>
                </a>
            </li>
            {% endfor %}
        </ul>
    </div>

    <div class="col-md-8">
        <h2>Add new task:</h2>
        <form class="form-inline" method="post" role="form">
            {{ form.hidden_tag() }}
            {{ wtf.form_errors(form, hiddens="only") }}
            {{ form.body(class="form-control", placeholder="New Task") }}
            {{ form.due(class="form-control", placeholder="dd/mm/yyy") }}
            {{ form.project_id(class="form-control")}}
            {{ form.submit(class="form-control") }}
        </form>
        <hr>
        <h2>My Tasks</h2>
        {% include "tasks/_tasks.html" %}
    </div>
</div>
{% endblock %}

This template includes the _tasks.html partial template below.

<table class="table">

    {% for task in tasks %}
    <tr>
        <td {% if task.done %}style="text-decoration: line-through;" {% endif %}>{{ task.body }}</td>
        <td>{{ task.due.date().__format__('%d/%m/%Y') }}</td>
        <td>
            <span class="label label-info">
                {{ task.project.name }}
            </span>
        </td>
        <td>
            {% if task.done %}
            <span  class="label label-default">Completed</span>
            {% else %}
            <a href="{{ url_for('tasks.complete', task_id=task.id) }}">
                <span  class="label label-primary">Done</span>
            </a>
            {% endif %}
        </td>
        <td>
            <a href="{{ url_for('tasks.delete', task_id=task.id) }}">
                <span class="label label-danger">Delete</span>
            </a>
        </td>
    </tr>
    {% endfor %}
</table>

When the user clicks on any of their projects in the sidebar, I want the list of tasks shown on this page to change and show only tasks that are assigned to the selected project. I've done this by creating this new view function project() that re-renders the templates above. But this requires a lot of reused code to validate the form and list out the users projects.

@tasks.route('projects/<int:project_id>', methods=['GET', 'POST'])
@login_required
def project(project_id):
    user = current_user
    form = NewTaskForm(user=current_user)
    if form.validate_on_submit():
        task = Task(body=form.body.data, due=form.due.data, author=current_user._get_current_object(), project=Project.query.filter_by(id=form.project_id.data).first())
        db.session.add(task)
        db.session.commit()
        flash('Your task has been added!')
    form.body.data = ''
    form.due.data = ''
    tasks = Task.query.filter_by(project_id=project_id)
    projects = current_user.projects
    return render_template('tasks/home.html', form=form, tasks=tasks, projects=projects)

What I'd really like to do is for project() to return a list of tasks that is passed to home() or something like that so I can get rid of all of all the repeated code. I only need to render the tasks again, all the other components on the page can stay the same.

Any advice on this would be much appreciated.

davidism
  • 121,510
  • 29
  • 395
  • 339
J Finer
  • 389
  • 1
  • 3
  • 10

1 Answers1

0

Your best solution would be ti use the same function for both routes, by making project_id an optional parameter for the home route. This question has more details, as well as links to examples in the Flask documentation to do so.

Having the project as an optional parameter would let you display all tasks in the home view when it is not present, and limit them to a given project when it is.

JSTL
  • 808
  • 1
  • 14
  • 25