Python Pyramid - Package Structure



The Cookiecutter utility automatically creates a package folder inside the parent project folder of the same name. The package folder consists of the following files and subfolders.

__init__.py

A folder needs __init__.py file for it to be treated as a Python package. The testproj package also has this file, which essentially declares the Pyramid WSGI application project for the development.ini to use it as the entry point.

The application object is returned by the main() function. It configures the application registry by including the template library chosen at the time of running cookiecutter, including the routes module and adding the views to the configurator by scanning the existing package. Following Python code is auto generated as __init__.py file.

from pyramid.config import Configurator
def main(global_config, **settings):
   """ This function returns a Pyramid WSGI application.
   """
   with Configurator(settings=settings) as config:
      config.include('pyramid_jinja2')
      config.include('.routes')
      config.include('.models')
      config.scan()
   return config.make_wsgi_app()

routes.py

The Cookiecutter utility automatically generates a Python script having a function called includeme(). It adds a static route and a home route pointing to '/' URL pattern.

def includeme(config):
   config.add_static_view('static', 'static', cache_max_age=3600)
   config.add_route('home', '/')

These routes are added to the application configuration by the main() function in __init__.py file explained above.

Views Package

The project package (in our case testproj package) contains this views subpackage - a folder containing a blank __init__.py, a Python module called default.py that contains definition a view function named my_view(). It sends the name of the project as a context to a pre-built template mytemplate.jinja2

from pyramid.view import view_config
from pyramid.response import Response
from sqlalchemy.exc import SQLAlchemyError
from .. import models

@view_config(route_name='home', renderer='testproj:templates/mytemplate.jinja2')
def my_view(request):
   try:
      query = request.dbsession.query(models.MyModel)
      one = query.filter(models.MyModel.name == 'one').one()
   except SQLAlchemyError:
      return Response(db_err_msg, content_type='text/plain', status=500)
   return {'one': one, 'project': 'testproj'}
   
db_err_msg = """\
Pyramid is having a problem using your SQL database.
....
"""

The default.py scripts also imports definition of mymodel in models subpackage. This views package also defines a notfound view in notfound.py file.

from pyramid.view import notfound_view_config
@notfound_view_config(renderer='testproj:templates/404.jinja2')
def notfound_view(request):
   request.response.status = 404
   return {}

Static Folder

This folder under the testproj package folder contains Pyramid logo files and theme.CSS for the homepage.

Templates Folder

We know that the web templates need to be stored in templates folder. This subfolder contains jinja2 templates. Here we have a base template named as layout.jinja2 and it is inherited by mytemplate.jinja2 to be rendered by my_view() view function.

{% 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 Pyramid application generated by<br><span class="font-normal">Cookiecutter</span>.</p>
</div>
{% endblock content %}

Models Package

This subpackage under the tesptproj package folder holds mymodel.py that has the definition of SQLAlchemy model named as MyModel.

from sqlalchemy import (
   Column,
   Index,
   Integer,
   Text,
)

from .meta import Base
class MyModel(Base):
   __tablename__ = 'models'
   id = Column(Integer, primary_key=True)
   name = Column(Text)
   value = Column(Integer)
Index('my_index', MyModel.name, unique=True, mysql_length=255)

The meta.py declares an object of Declarative Base class in SQLAlchemy.

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import MetaData

NAMING_CONVENTION = {
   "ix": "ix_%(column_0_label)s",
   "uq": "uq_%(table_name)s_%(column_0_name)s",
   "ck": "ck_%(table_name)s_%(constraint_name)s",
   "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
   "pk": "pk_%(table_name)s"
}
metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
Advertisements