Python Pyramid – Using SQLAlchemy ”; Previous Next In this chapter, we shall learn how to use a relational database as a back-end with the Pyramid web application. Python can interact with almost every relational database using corresponding DB-API compatible connector modules or drivers. However, we shall use SQLAlchemy library as an interface between Python code and a database (we are going to use SQLite database as Python has in-built support for it). SQLAlchemy is a popular SQL toolkit and Object Relational Mapper. Object Relational Mapping is a programming technique for converting data between incompatible type systems in object-oriented programming languages. Usually, the type system used in an Object Oriented language like Python contains non-scalar types. However, data types in most of the database products such as Oracle, MySQL, etc., are of primitive types such as integers and strings. In an ORM system, each class maps to a table in the underlying database. Instead of writing tedious database interfacing code yourself, an ORM takes care of these issues for you while you can focus on programming the logics of the system. In order to use SQLALchemy, we need to first install the library using PIP installer. pip install sqlalchemy SQLAlchemy is designed to operate with a DBAPI implementation built for a particular database. It uses dialect system to communicate with various types of DBAPI implementations and databases. All dialects require that an appropriate DBAPI driver is installed. The following are the dialects included − Firebird Microsoft SQL Server MySQL Oracle PostgreSQL SQLite Sybase Database Engine Since we are going to use SQLite database, we need to create a database engine for our database called test.db. Import create_engine() function from the sqlalchemy module. from sqlalchemy import create_engine from sqlalchemy.dialects.sqlite import * SQLALCHEMY_DATABASE_URL = “sqlite:///./test.db” engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args = {“check_same_thread”: False}) In order to interact with the database, we need to obtain its handle. A session object is the handle to database. Session class is defined using sessionmaker() – a configurable session factory method which is bound to the engine object. from sqlalchemy.orm import sessionmaker, Session session = sessionmaker(autocommit=False, autoflush=False, bind=engine) Next, we need a declarative base class that stores a catalog of classes and mapped tables in the Declarative system. from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() Model Class Students, a subclass of Base, is mapped to a students table in the database. Attributes in the Students class correspond to the data types of the columns in the target table. Note that the id attribute corresponds to the primary key in the book table. class Students(Base): __tablename__ = ”student” id = Column(Integer, primary_key=True, nullable=False) name = Column(String(63), unique=True) marks = Column(Integer) Base.metadata.create_all(bind=engine) The create_all() method creates the corresponding tables in the database. It can be confirmed by using a SQLite Visual tool such as SQLiteStudio. We shall now define view functions for performing CRUD operations (i.e. add, display, modify and delete rows) on the student table in the above database. Add a New Student Record First, we shall create a HTML form template for the user to enter student data and define a view that renders the template. Here is the myform.html template Example <html> <body> <form method=”POST” action=”http://localhost:6543/add”> <p>Student Id: <input type=”text” name=”id”/> </p> <p>student Name: <input type=”text” name=”name”/> </p> <p>Percentage: <input type=”text” name=”percent”/> </p> <p><input type=”submit” value=”Submit”> </p> </body> </html> In the Pyramid application code, define the index() view function to render the above form. from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response from pyramid.view import view_config @view_config(route_name=”index”, renderer=”templates/myform.html”) def index(request): return {} In the application configuration, register the route with the “/new” pattern for this view as − if __name__ == ”__main__”: with Configurator() as config: config.include(”pyramid_jinja2”) config.add_jinja2_renderer(“.html”) config.add_route(”index”, ”/new”) config.scan() app = config.make_wsgi_app() server = make_server(”0.0.0.0”, 6543, app) server.serve_forever() As the HTML form in the above template is submitted to /add URL with POST action, we need to map this URL to add route and register add() view that parses the form data into an object of Students class. This object is added to the database session and the operation is finalized by calling its commit() method. @view_config(route_name=”add”, request_method=”POST”) def add(request): id=request.POST[”id”] name=request.POST[”name”] percent=int(request.POST[”percent”]) student=Students(id=id, name=name, percent=percent) session.add(student) session.commit() return HTTPFound(location=”http://localhost:6543/”) Make sure that the add route is added in the configuration, mapped to /add URL pattern. config.add_route(”add”,”/add”) Output If we start the server and open http://localhost:6543/new in the browser, the Entry form will be displayed as follows − Fill the form and press the “submit” button. The add() view will be called and a new record will be added in the students table. Repeat the process a couple of times to add a few records. Here is a sample data − Show List of All Records All the objects of the Students model (corresponding to row in students table) are obtained by querying the model. rows = session.query(Students).all() Each row is converted into a dict object, all of them are appended to a list of dict objects, and returned as a context to the list.html template to be displayed in the form of HTML template. The process is performed by the showall() view function, associated with list route. @view_config(route_name=”list”, renderer=”templates/marklist.html”) def showall(request): rows = session.query(Students).all() students=[] for row in rows: students.append({“id”:row.id, “name”:row.name, “percent”:row.percent}) return{”students”:students} Example The marklist.html template renders the Students list as a HTML table. Its HTML/jinja2 script is as follows − <html> <body> <table border=1> <thead> <tr> <th>Student ID</th> <th>Student Name</th> <th>percentage</th> <th>Edit</th> <th>Delete</th> </tr> </thead> <tbody> {% for Student in students %} <tr> <td>{{ Student.id }}</td> <td>{{ Student.name }}</td> <td>{{ Student.percent }}</td>
Category: python Pyramid
Python Pyramid – Request Object ”; Previous Next The functionality of a view callable involves obtaining the request data from the WSGI environment and returning a certain HTTP response back to the client after processing. The view function receives the Request object as the argument. Normally this object is not instantiated by the user. Instead, it encapsulates the WSGI environ dictionary. This request object represents “pyramid.request.Request class.” It possesses a number of attributes and methods, using which the request data is processed by the view function. Here are some of the attributes − request.method − The HTTP request method used by the client to send the data, e.g., GET, POST request.GET − This attribute is a multidict with all the variables in the query string. request.POST − This attribute is available only if the request was a POST and it is a form submission. It is a multidict with all the variables in the request body. request.params − A combined multidict of everything in request.GET and request.POST. request.body − This attribute contains the entire request body as a string. This is useful when the request is a POST that is not a form submission, or a request like a PUT. request.cookies − Contains all the cookies. request.headers − A case-insensitive dictionary of all the HTTP headers. In addition to the above HTTP specific environment attributes, Pyramid also adds certain special attributes. request.url − Returns the full request URL with query string, e.g., http://localhost:6543/app?name=Ravi request.host − The host information in the URL, e.g., localhost request.host_url − This attribute returns the URL with the host, e.g., http://localhost:6543/ request.application_url − The URL of the application (without PATH_INFO), e.g., http://localhost:6543/app request.path_url − Contains the URL of the application including the PATH_INFO, e.g., http://localhost:66543/app request.path − Returns The URL including PATH_INFO without the host, e.g., “/app” request.path_qs − the query string in the URL including PATH_INFO, e.g., “/app?name=Ravi” request.query_string − Only the query string in the URL, e.g., “name=Ravi” Print Page Previous Next Advertisements ”;
Python Pyramid – Static Assets ”; Previous Next Often it is required to include in the template response some resources that remain unchanged even if there is a certain dynamic data. Such resources are called static assets. Media files (.png, .jpg etc), JavaScript files to be used for executing some front end code, or stylesheets for formatting HTML (.css files) are the examples of static files. Pyramid serves these static assets from a designated directory in the server”s filesystem to the client”s browser. The add_static_view() method of the Configurator object defines the name of the route and path for the folder containing the static files such as images, JavaScript, and CSS files. As a convention, the ”static” directory is used to store the static assets and the add_static_view() is used as follows − config.add_static_view(name=”static”, path=”static”) Once the static route is defined, the path of static assets while using in HTML script can be obtained by request.static_url() method. Static Image In the following example, Pyramid logo is to be rendered in the logo.html template. Hence, “pyramid.png” file is first placed in static folder. It is now available for using as src attribute of <img> tag in HTML code. <html> <body> <h1>Hello, {{ name }}. Welcome to Pyramid</h1> <img src=”{{request.static_url(”app:static/pyramid.png”)}}”> </body> </html> Example The application code updates the configurator with add_static_view(), and defines index() view renders the above template. from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response from pyramid.view import view_config @view_config(route_name=”index”, renderer=”templates/logo.html”) def index(request): return {”name”:request.matchdict[”name”]} if __name__ == ”__main__”: with Configurator() as config: config.include(”pyramid_jinja2”) config.add_jinja2_renderer(“.html”) config.add_route(”index”, ”/{name}”) config.add_static_view(name=”static”, path=”app:static”) config.scan() app = config.make_wsgi_app() server = make_server(”0.0.0.0”, 6543, app) server.serve_forever() Output Run the above code to start the server. Use http://localhost:6543/Guest as the URL in your browser. Here ”Guest” is the path parameter picked up by the view function in matchdict object and passed to the logo.html template as the context. The browser displays the Pyramid logo now. Javascript as Static Asset Here is another example of static file. A JavaScript code hello.js contains a definition of myfunction() to be executed on the onload event in following HTML script (templateshello.html) <html> <head> <script src=”{{request.static_url(”app:static/hello.js”)}}”></script> </head> <body onload=”myFunction()”> <div id=”time” style=”text-align:right; width=”100%”></div> <h1><div id=”ttl”>{{ name }}</div></h1> </body> </html> Example The hello.js code saved in static folder is as follows − function myFunction() { var today = new Date(); var h = today.getHours(); var m = today.getMinutes(); var s = today.getSeconds(); var msg=””; if (h<12) { msg=”Good Morning, “; } if (h>=12 && h<18) { msg=”Good Afternoon, “; } if (h>=18) { msg=”Good Evening, “; } var x=document.getElementById(”ttl”).innerHTML; document.getElementById(”ttl”).innerHTML = msg+x; document.getElementById(”time”).innerHTML = h + “:” + m + “:” + s; } Output The function detects the value of current time and assigns appropriate value to msg variable (good morning, good afternoon or good evening) depending on the time of the day. Save hello.js in static folder, hello.html in templates folder and restart the server. The browser should show the current time and corresponding message below it. Print Page Previous Next Advertisements ”;
Creating A Project Manually
Python Pyramid – Creating A Project Manually ”; Previous Next The Cookiecutter utility uses pre-defined project templates to auto-generate the project and package structure. For complex projects, it saves a lot of manual effort in properly organizing various project components. However, a Pyramid project can be built manually without having to use Cookiecutter. In this section, we shall see how a Pyramid project named Hello is built in following easy steps. setup.py Create a project directory within Pyramid virtual environment. md hello cd hello And save the following script as setup.py from setuptools import setup requires = [ ”pyramid”, ”waitress”, ] setup( name=”hello”, install_requires=requires, entry_points={ ”paste.app_factory”: [ ”main = hello:main” ], }, ) As mentioned earlier, this is a Setuptools setup file that defines requirements for installing dependencies for your package. Run the following command to install the project and generate the ”egg” in the name hello.egg-info. pip3 install -e. development.ini Pyramid uses PasteDeploy configuration file mainly to specify the main application object, and the server configuration. We are going to use the application object in the egg info of hello package, and the Waitress server, listening on port 5643 of the localhost. Hence, save the following snippet as development.ini file. [app:main] use = egg:hello [server:main] use = egg:waitress#main listen = localhost:6543 __init__.py Finally, the application code resides in this file which is also essential for the hello folder to be recognised as a package. The code is a basic Hello World Pyramid application code having hello_world() view. The main() function registers this view with hello route having ”/” URL pattern, and returns the application object given by make_wsgi_app() method of Configurator. from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response(”<body><h1>Hello World!</h1></body>”) def main(global_config, **settings): config = Configurator(settings=settings) config.add_route(”hello”, ”/”) config.add_view(hello_world, route_name=”hello”) return config.make_wsgi_app() Finally, serve the application with the help of pserve command. pserve development.ini –reload Print Page Previous Next Advertisements ”;
Python Pyramid – Security
Python Pyramid – Security ”; Previous Next Pyramid”s declarative security system determines the identity of the current user and verifies if the user has access to certain resources. The security policy can prevent the user from invoking a view. Before any view is invoked, the authorization system uses the credentials in the request to determine if access will be allowed. The security policy is defined as a class that controls the user access with the help of following methods defined in pyramid.security module − forget(request) − This method returns header tuples suitable for ”forgetting” the set of credentials possessed by the currently authenticated user. It is generally used within the body of a view function. remember(request, userid) − This method returns a sequence of header tuples on the request”s response. They are suitable for ”remembering” a set of credentials such as userid using the current security policy. Common usage might look like so within the body of a view function. The authenticated user”s access is controlled by the objects of Allowed and Denied classes in this module. To implement the functionality of identity, remember and forget mechanism, Pyramid provides the following helper classes defined in the pyramid.authentication module − SessionAuthenticationHelper − Store the userid in the session. AuthTktCookieHelper − Store the userid with an “auth ticket” cookie. We can also use extract_http_basic_credentials() function to retrieve user credentials using HTTP Basic Auth. To retrieve the userid from REMOTE_USER in the WSGI environment, the request.environ.get(”REMOTE_USER”) can be used. Example Let us now learn how to implement the security policy with the help of following example. The “development.ini” for this example is as follows − [app:main] use = egg:tutorial pyramid.reload_templates = true pyramid.includes = pyramid_debugtoolbar hello.secret = a12b [server:main] use = egg:waitress#main listen = localhost:6543 We then write the security policy class in the following Python code saved as security.py − from pyramid.authentication import AuthTktCookieHelper USERS = {”admin”: ”admin”, ”manager”: ”manager”} class SecurityPolicy: def __init__(self, secret): self.authtkt = AuthTktCookieHelper(secret=secret) def identity(self, request): identity = self.authtkt.identify(request) if identity is not None and identity[”userid”] in USERS: return identity def authenticated_userid(self, request): identity = self.identity(request) if identity is not None: return identity[”userid”] def remember(self, request, userid, **kw): return self.authtkt.remember(request, userid, **kw) def forget(self, request, **kw): return self.authtkt.forget(request, **kw) The __init__.py file in our package folder defines following configuration. The security policy class defined above is added in the configuration with set_security_policy() method of Configurator class. Three routes – home, login and logout – are added to the configuration. from pyramid.config import Configurator from .security import SecurityPolicy def main(global_config, **settings): config = Configurator(settings=settings) config.include(”pyramid_chameleon”) config.set_security_policy( SecurityPolicy( secret=settings[”hello.secret”], ), ) config.add_route(”home”, ”/”) config.add_route(”login”, ”/login”) config.add_route(”logout”, ”/logout”) config.scan(”.views”) return config.make_wsgi_app() Three views corresponding to the above routes are defined in views.py. from pyramid.httpexceptions import HTTPFound from pyramid.security import remember, forget from pyramid.view import view_config, view_defaults from .security import USERS @view_defaults(renderer=”home.pt”) class HelloViews: def __init__(self, request): self.request = request self.logged_in = request.authenticated_userid @view_config(route_name=”home”) def home(self): return {”name”: ”Welcome”} @view_config(route_name=”login”, renderer=”login.pt”) def login(self): request = self.request login_url = request.route_url(”login”) referrer = request.url if referrer == login_url: referrer = ”/” came_from = request.params.get(”came_from”, referrer) message = ”” login = ”” password = ”” if ”form.submitted” in request.params: login = request.params[”login”] password = request.params[”password”] pw = USERS.get(login) if pw == password: headers = remember(request, login) return HTTPFound(location=came_from, headers=headers) message = ”Failed login” return dict( name=”Login”, message=message, url=request.application_url + ”/login”, came_from=came_from, login=login, password=password,) @view_config(route_name=”logout”) def logout(self): request = self.request headers = forget(request) url = request.route_url(”home”) return HTTPFound(location=url, headers=headers) The login view renders the login form. When the user Id and password entered by the user are verified against the list of USERS, the details are ”remembered”. On the other hand, the logout view releases these details by ”forgetting”. The home view renders the following chameleon template – home.pt <!DOCTYPE html> <html lang=”en”> <body> <div> <a tal:condition=”view.logged_in is None” href=”${request.application_url}/login”>Log In</a> <a tal:condition=”view.logged_in is not None” href=”${request.application_url}/logout”>Logout</a> </div> <h1>Hello. ${name}</h1> </body> </html> Following is the chameleon template login.pt for login view. <!DOCTYPE html> <html lang=”en”> <body> <h1>Login</h1> <span tal:replace=”message”/> <form action=”${url}” method=”post”> <input type=”hidden” name=”came_from” value=”${came_from}”/> <label for=”login”>Username</label> <input type=”text” id=”login” name=”login” value=”${login}”/><br/> <label for=”password”>Password</label> <input type=”password” id=”password” name=”password” value=”${password}”/><br/> <input type=”submit” name=”form.submitted” value=”Log In”/> </form> </body> </html> The development.ini and setup.py are placed in the outer project folder, while, the __init__.py, views.py, security.py and the templates home.pt as well as login.pt should be saved under the package folder named hello. Install the package with the following command − Envhello>pip3 install -e. Start the server with the pserve utility. pserve development.ini Output Open the browser and visit http://localhost:6543/ link. Click the “Log In” link to open the login form − The home view page comes back with the link changed to logout as the credentials are remembered. Clicking the “logout” link will result in forgetting the credentials and the default home page will be shown. Print Page Previous Next Advertisements ”;
Python Pyramid – Creating A Project ”; Previous Next It is assumed that a Pyramid virtual environment is up and running, and Cookiecutter is installed in it. The easiest way to create a Cookiecutter project is to use a pre-built starter template as per the following command − cookiecutter gh:Pylons/pyramid-cookiecutter-starter –checkout 2.0-branch The template is downloaded and the user is asked about his choice of name of the project − project_name [Pyramid Scaffold]: testproj repo_name [testproj]: Then choose the template language. Select template_language − 1 – jinja2 2 – chameleon 3 – mako Choose from 1, 2, 3 [1]: 1 Since we are familiar with jinja2, give 1 as the choice. Next, use SQLALchemy as the backend. Select backend: 1 – none 2 – sqlalchemy 3 – zodb Choose from 1, 2, 3 [1]: 2 Inside the testproj folder, following file structure is created − │ development.ini │ MANIFEST.in │ production.ini │ pytest.ini │ README.txt │ setup.py │ testing.ini │ ├───testproj │ │ pshell.py │ │ routes.py │ │ __init__.py │ │ │ ├───alembic │ │ │ env.py │ │ │ script.py.mako │ │ │ │ │ └───versions │ │ README.txt │ │ │ ├───models │ │ meta.py │ │ mymodel.py │ │ __init__.py │ │ │ ├───scripts │ │ initialize_db.py │ │ __init__.py │ │ │ ├───static │ │ pyramid-16×16.png │ │ pyramid.png │ │ theme.css │ │ │ ├───templates │ │ 404.jinja2 │ │ layout.jinja2 │ │ mytemplate.jinja2 │ │ │ └───views │ default.py │ notfound.py │ __init__.py │ └───tests conftest.py test_functional.py test_views.py __init__.py The outer testproj folder has an inner testproj package subfolder and tests package. The inner testproj subfolder is a package having models and scripts, subpackages, and static as well as templates folders. Next, initialize and upgrade the database using Alembic. # Generate your first revision. alembic -c development.ini revision –autogenerate -m “init” # Upgrade to that revision. alembic -c development.ini upgrade head Alembic is a lightweight database migration tool for usage with the SQLAlchemy Database Toolkit for Python. The outer project folder will now show a testproj.sqlite database. The development.ini file provides a default data for the database. Populate the database with it by the following command. initialize_testproj_db development.ini The Cookiecutter utility also generates the test suite in the tests package. They are based on PyTest package. Go ahead and see if the tests pass. Pytest ================ test session starts ====================== platform win32 — Python 3.10.1, pytest-7.1.2, pluggy-1.0.0 rootdir: F:pyram-envtestproj, configfile: pytest.ini, testpaths: testproj, tests plugins: cov-3.0.0 collected 5 items teststest_functional.py .. [ 40%] teststest_views.py … [100%] =============== 5 passed, 20 warnings in 6.66s =============== Cookiecutter uses the Waitress server. The Pyramid application is served on localhost”s port 6543 by following command − pserve development.ini Starting server in PID 67700. 2022-06-19 23:43:51,308 INFO [waitress:485][MainThread] Serving on http://[::1]:6543 2022-06-19 23:43:51,308 INFO [waitress:485][MainThread] Serving on http://127.0.0.1:6543 Open the browser and visit http://localhost:6543/ in it. The homepage of the newly created project will be displayed as follows − Debug Toolbar You can find a smaller Pyramid logo at the top right of the homepage. Click on it to open a new tab and a debug toolbar that provides lots of useful information about the project. For example, the SQLAlchemy tab under the history heading shows the SQLAlchemy queries showing the structure of the model created from the default data in development.ini. The Global heading again shows tabs such as Introspection, Routes, etc. as shown below. Click the “Routes” tab to see the routes and their matching patterns defined in the application”s configuration. Print Page Previous Next Advertisements ”;
Pyramid – HTML Form Template
Python Pyramid – HTML Form Template ”; Previous Next In this chapter, we shall see how Pyramid reads the data from HTML form. Let us save the following HTML script as myform.html. We shall use it for obtaining Template object and render it. <html> <body> <form method=”POST” action=”http://localhost:6543/students”> <p>Student Id: <input type=”text” name=”id”/> </p> <p>student Name: <input type=”text” name=”name”/> </p> <p>Percentage: <input type=”text” name=”percent”/> </p> <p><input type=”submit” value=”Submit”> </p> </body> </html> An “index” route added in Pyramid object”s configuration is mapped to the following index() function, which renders the above HTML form − @view_config(route_name=”index”, renderer=”templates/myform.html”) def index(request): return {} As we can see, the data entered by user is passed to /students URL by POST request. So, we shall add a ”students” route to match the /students pattern, and associate it with add() view function as follows − @view_config(route_name=”students”, renderer=”templates/marklist.html”) def add(request): student={”id”:request.params[”id”], ”name”:request.params[”name”], ”percent”:int(request.params[”percent”])} 9. Pyramid – HTML Form Template students.append(student) return {”students”:students} The data sent by POST request is available in the HTTP request object in the form of request.params object. It is a dictionary of HTML form attributes and their values as entered by the user. This data is parsed and appended to students list of dictionary objects. The updated students object is passed to the marklist.html template as a context data. The marklist.html web template as the same as used in the previous example. It displays a table of student data along with the computed result column. <html> <body> <table border=1> <thead> <tr> <th>Student ID</th> <th>Student Name</th> <th>percentage</th> <th>Result</th> </tr> </thead> <tbody> {% for Student in students %} <tr> <td>{{ Student.id }}</td> <td>{{ Student.name }}</td> <td>{{ Student.percent }}</td> <td> {% if Student.percent>=50 %} Pass {% else %} Fail {% endif %} </td> </tr> {% endfor %} </tbody> </table> </body> </html> Example The complete code containing views for rendering the HTML form, parsing the form data and generating a page showing the students marklist table is given below − from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response from pyramid.view import view_config students = [ {“id”: 1, “name”: “Ravi”, “percent”: 75}, {“id”: 2, “name”: “Mona”, “percent”: 80}, {“id”: 3, “name”: “Mathews”, “percent”: 45}, ] @view_config(route_name=”index”, renderer=”templates/myform.html”) def index(request): return {} @view_config(route_name=”students”, renderer=”templates/marklist.html”) def add(request): student={”id”:request.params[”id”], ”name”:request.params[”name”], ”percent”:int(request.params[”percent”])} students.append(student) return {”students”:students} if __name__ == ”__main__”: with Configurator() as config: config.include(”pyramid_jinja2”) config.add_jinja2_renderer(“.html”) config.add_route(”index”, ”/”) config.add_route(”students”,”/students”) config.scan() app = config.make_wsgi_app() server = make_server(”0.0.0.0”, 6543, app) server.serve_forever() Output To start the server, run the above Python code from command line. In your browser, visit http://localhost:6543/ to get the form as shown below − Enter a sample data as shown and press submit button. The browser is directed to /students URL, which in turn invokes the add() view. The result is a table of marklist showing the newly entered data of a new student. Print Page Previous Next Advertisements ”;
Python Pyramid – Events
Python Pyramid – Events ”; Previous Next A Pyramid application emits various events during the course of its lifetime. Although these events need not be used up normally, slightly advanced operations can be performed by properly handling these events. An event broadcast by the Pyramid framework becomes usable only when you register it with a subscriber function. The emitted event must be used as the argument of the subscriber function. def mysubscriber(event): print(“new request”) However, a subscriber function becomes operational only when it is added to the application”s configuration with the help of add_subscriber() method as shown below − In the following snippet, the application is configured so that the subscriber function is invoked when it emits NewRequest object. from pyramid.events import NewRequest config.add_subscriber(mysubscriber, NewRequest) There is also a @subscriber() decorator for configuring the event. from pyramid.events import NewRequest from pyramid.events import subscriber @subscriber(NewRequest) def mysubscriber(event): print (“new request”) As with the decortive view configuration, here also the config.scan() must be performed for the decorator to be effective. As mentioned earlier, the Pyramid application emits a variety of event types. These event classes are available in pyramid.event module. They are listed below − ApplicationCreated − This event is transmitted just when the config.make_wsgi_app() method of the Configurator class is called to return the WSGI application object. NewRequest − An object of this event class is emitted every time the Pyramid application starts processing an incoming request. This object has a request attribute which is the request object as supplied by WSGI environ dictionary. ContextFound − The application”s router traverses all the routes and finds an appropriate match with the URL pattern. This is when the object of ContextFound class is instantiated. BeforeTraversal − An instance of this class is emitted as an event after the Pyramid router has attempted to find a route object but before any traversal or view code is executed. NewResponse − As the name suggests, this event is raised whenever any Pyramid view callable returns a response. This object has request and response attributes. BeforeRender − An object of this type is transmitted as an event just before a renderer is invoked. The subscriber function to this event has access to the application”s global data (which is in the form of a dict object) and can modify value of one or more keys. Print Page Previous Next Advertisements ”;
Python Pyramid – Response Object ”; Previous Next The Response class is defined in pyramid.response module. An object of this class is returned by the view callable. from pyramid.response import Response def hell(request): return Response(“Hello World”) The response object contains a status code (default is 200 OK), a list of response headers and the response body. Most HTTP response headers are available as properties. Following attributes are available for the Response object − response.content_type − The content type is a string such as – response.content_type = ”text/html”. response.charset − It also informs encoding in response.text. response.set_cookie − This attribute is used to set a cookie. The arguments needed to be given are name, value, and max_age. response.delete_cookie − Delete a cookie from the client. Effectively it sets max_age to 0 and the cookie value to ””. The pyramid.httpexceptions module defines classes to handle error responses such as 404 Not Found. These classes are in fact subclasses of the Response class. One such class is “pyramid.httpexceptions.HTTPNotFound”. Its typical use is as follows − from pyramid.httpexceptions import HTTPNotFound from pyramid.config import view_config @view_config(route=”Hello”) def hello(request): response = HTTPNotFound(“There is no such route defined”) return response We can use location property of Response class to redirect the client to another route. For example − view_config(route_name=”add”, request_method=”POST”) def add(request): #add a new object return HTTPFound(location=”http://localhost:6543/”) Print Page Previous Next Advertisements ”;
Python Pyramid – Testing
Python Pyramid – Testing ”; Previous Next Writing test scripts which ensure that your code works correctly is considered as a good programming practice. Python ecosystem had a number of testing frameworks, including unittest which is bundled in the standard library. Pytest is a popular testing library. It is a preferred library for Pyramid projects. We shall use the hello package that we developed earlier while demonstrating the use of PasteDeploy configuration. First, ensure that the Pyramid environment has PyTest package installed. pip3 install pytest Open the setup.py file in hello package and modify it by adding the lines shown in bold. from setuptools import setup requires = [ ”pyramid”, ”waitress”, ] dev_requires = [”pytest”,] setup( name=”hello”, install_requires=requires, extras_require={ ”dev”: dev_requires, }, entry_points={ ”paste.app_factory”: [ ”main = hello:main” ], }, ) Here, Pytest is added as the project dependency whenever it is installed (or reinstalled) using following command − pip3 install -e “.[dev] Store the following Python code as testing.py in hello package. import unittest from pyramid import testing class HelloTests(unittest.TestCase): def test_hello_world(self): from . import hello_world request = testing.DummyRequest() response = hello_world(request) self.assertEqual(response.status_code, 200) To run the tests, use following Pytest command. The output of the test is shown below − Envhello>pytest tests.py ========================== test session starts ========================== platform win32 — Python 3.10.1, pytest-7.1.2, pluggy-1.0.0 rootdir: E:tp-pyramidhello collected 1 item tests.py. [100%] =========================== 1 passed in 1.12s =========================== To check if the test fails, induce an error in the test function and run again. (tp-pyramid) E:tp-pyramidhello>pytest tests.py ========================== test session starts ========================== collected 1 item tests.py F [100%] =============================== FAILURES ================================ ______________________ HelloTests.test_hello_world ______________________ self = <hello.tests.HelloTests testMethod=test_hello_world> def test_hello_world(self): from . import hello_world request = testing.DummyRequest() response = hello_world(request) > self.assertEqual(response.status_code, 404) E AssertionError: 200 != 404 tests.py:13: AssertionError ======================== short test summary info ======================== FAILED tests.py::HelloTests::test_hello_world – AssertionError: 200 != 404 =========================== 1 failed in 1.53s =========================== Functional Testing Although Unit tests are popularly used in test-driven development (TDD)approach, for web applications, WebTest is a Python package that does functional testing. We can simulate a full HTTP request against a WSGI application, then test the information in the response. Example Let us use the hello project that we had used in the earlier example. Open the setup.py and add WebTest as the project dependency. from setuptools import setup requires = [ ”pyramid”, ”waitress”, ] dev_requires = [”pytest”,”webtest”,] setup( name=”hello”, install_requires=requires, extras_require={ ”dev”: dev_requires, }, entry_points={ ”paste.app_factory”: [ ”main = hello:main” ], }, ) Reinstall the hello package and its new dependency for development mode. Envhello>..scriptspip3 install -e “.[dev]” Include a functional test in tests.py file import unittest from pyramid import testing class HelloTests(unittest.TestCase): def test_hello_world(self): from . import hello_world request = testing.DummyRequest() response = hello_world(request) self.assertEqual(response.status_code, 200) class HelloFunctionalTests(unittest.TestCase): def setUp(self): from . import main app = main({}) from webtest import TestApp self.testapp = TestApp(app) def test_hello_world(self): res = self.testapp.get(”/”, status=200) self.assertIn(b”<h1>Hello World!</h1>”, res.body) Output Finally run Pytest as per the following command − Envhello>pytest tests.py ========================== test session starts ========================== platform win32 — Python 3.10.1, pytest-7.1.2, pluggy-1.0.0 rootdir: E:tp-pyramidhello collected 2 items tests.py .. [100%] =========================== 2 passed in 2.37s =========================== Tests in Cookiecutter Project The CookieCutter utility auto-generates the tests package containing functional tests and unit tests. We had earlier used Cookiecutter to build Pyramid project named testproj. In this project, we find tests folder. Example The test_functional py contains the following test functions − from testproj import models def test_my_view_success(testapp, dbsession): model = models.MyModel(name=”one”, value=55) dbsession.add(model) dbsession.flush() res = testapp.get(”/”, status=200) assert res.body def test_notfound(testapp): res = testapp.get(”/badurl”, status=404) assert res.status_code == 404 The test_views.py defines following test functions to test the views − from testproj import models from testproj.views.default import my_view from testproj.views.notfound import notfound_view def test_my_view_failure(app_request): info = my_view(app_request) assert info.status_int == 500 def test_my_view_success(app_request, dbsession): model = models.MyModel(name=”one”, value=55) dbsession.add(model) dbsession.flush() info = my_view(app_request) assert app_request.response.status_int == 200 assert info[”one”].name == ”one” assert info[”project”] == ”testproj” def test_notfound_view(app_request): info = notfound_view(app_request) assert app_request.response.status_int == 404 assert info == {} Output These tests are run by the following command − Envtestproj>Pytest ========================== test session starts ========================== platform win32 — Python 3.10.1, pytest-7.1.2, pluggy-1.0.0 rootdir: Envtestproj, configfile: pytest.ini, testpaths: testproj, tests plugins: cov-3.0.0 collected 5 items teststest_functional.py .. [ 40%] teststest_views.py … [100%] =============== 5 passed, 20 warnings in 6.66s =============== Print Page Previous Next Advertisements ”;