Posted on

Pyramid 04: Edit (update) and delete items from database

Add this step we finish the basic crud functions of our web application by adding views to update and delete documents on database and creating the necessary jinja2 templates for it.

First let’s create a form to update an item. The forms.py should be:

from wtforms import Form, StringField, IntegerField, validators
from wtforms.widgets import HiddenInput


class TaskForm(Form):
    name = StringField('Name', [validators.Length(min=4, max=25)])
    active = IntegerField('Active', [validators.InputRequired()])


class TaskUpdateForm(TaskForm):
    id = StringField(widget=HiddenInput())

At views.py add TaskUpdateForm to the import. We have to add ObjectId to the imports in order to access an item on the mongodb. The file views.py should look like:

from bson.objectid import ObjectId
from pyramid.httpexceptions import HTTPFound
from pyramid.url import route_url
from pyramid.view import view_config

from .forms import TaskForm, TaskUpdateForm


@view_config(route_name='home', renderer='templates/home.jinja2')
def task_list(request):
    tasks = request.db['tasks'].find()
    return {
        'tasks': tasks,
        'project': 'task_manager',
    }


@view_config(route_name='tadd', renderer='templates/add.jinja2')
def task_add(request):
    form = TaskForm(request.POST, None)

    if request.POST and form.validate():
        entry = form.data
        request.db['tasks'].save(entry)
        return HTTPFound(route_url('home', request))

    return {'form': form}


@view_config(route_name='tedit', renderer='templates/edit.jinja2')
def task_edit(request):

    id = request.matchdict.get('id', None)
    item = request.db['tasks'].find_one({'_id': ObjectId(id)})
    form = TaskUpdateForm(request.POST,
                          id=id, name=item['name'],
                          active=item['active'])

    if request.method == 'POST' and form.validate():
        entry = form.data
        entry['_id'] = ObjectId(entry.pop('id'))
        request.db['tasks'].save(entry)
        return HTTPFound(route_url('home', request))

    return {'form': form}


@view_config(route_name='tdelete')
def task_delete(request):
    id = request.matchdict.get('id', None)
    if id:
        request.db['tasks'].remove({'_id': ObjectId(id)})
    return HTTPFound(route_url('home', request))

Configure the routes at routes.py. The final file should be:

def includeme(config):
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('home', '/')
    config.add_route('tadd', '/add')
    config.add_route('tedit', '/edit/{id}')
    config.add_route('tdelete', '/delete/{id}')

Create the template edit.jinja2 inside the folder templates:

{% extends "layout.jinja2" %}


{% block content %}
<form action="" method="post">
    {{ form.id }}
    <div>
        {{ form.name.label }}: {{ form.name(class="text-primary") }}
        {% if form.name.errors %}
            <ul class="errors">
            {% for error in form.name.errors %}
            <li>{{ error }}</li>
            {% endfor %}
            </ul>
        {% endif %}
    </div>
    <div>
        {{ form.active.label}}: {{ form.active(class="text-primary") }}
        {% if form.active.errors %}
            <ul class="errors">
            {% for error in form.active.errors %}
            <li>{{ error }}</li>
            {% endfor %}
            </ul>
        {% endif %}        
    </div>
    <input type="submit" value="Submit">
</form>
{% endblock content %}

Add links to edit and delete items at home.jinja2. The final file should be:

{% extends "layout.jinja2" %}

{% block content %}
<div class="content">
  <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1>
  <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a&nbsp;Pyramid application
    generated&nbsp;by<br><span class="font-normal">Cookiecutter</span>.</p>
</div>
<hr>
<h1> Tasks</h1> <br>
<ul>
  {% if tasks %}

  {% else %}
  <li>No tasks</li>
  {% endif %}
  {% for task in tasks %}
  <li>
    {{task.name}} | <a href="{{ request.route_url('tedit', id=task['_id'])}}">Edit</a> | <a href="{{ request.route_url('tdelete', id=task['_id'])}}">Delete</a>
  </li>

  {% endfor %}
</ul>
<h2>Actions</h2>
<ul>
    <li><a href="{{request.route_url('tadd')}}">Add new Task</a></li>
</ul>
{% endblock content %}

Go to the folder where the development.ini file is and run the project:

env/bin/pserve development.ini --reload

Now we have a CRUD with Pyramid, Mongodb (pymongo) and WTForms.

You can find the code for this part of the project at the part04 branch of the project repository.
To clone and run the specific branch use:

git clone -b part04 https://github.com/albertosdneto/tutorial_pyramid_mongo.git
cd tutorial_pyramid_mongo/
python3 -m venv env
env/bin/pip install --upgrade pip setuptools
env/bin/pip install -e .
env/bin/pserve development.ini --reload

There is a lot to improve in order for it to be ready for production and I will cover the necessary steps along the weeks as I learn it.

I hope you enjoy.