4.4. Pyramid Plugin
4.4.1. About
Pyramid Plugin is a simple set of features:
- Extended Configurator and plugins - allows to create
wsgi object for uwsgi or gunicorn frameworks to use
- Routing Wrapper - allows to get view value from class
variables
Views - view classes with support of http methods
All these features can be used separately.
4.4.2. Extended Configurator
Pyramid Framework gives you ability to create wsgi application, which can be used by the uwsgi/gunicorn/etc. frameworks. Pyramid is using Paster to configure the wsgi application, this means you need to configure some stuff in the Paster/Pyramid way.
4.4.2.1. Application Implementation
Implementing of the Application is pretty simple. The only thing which needed to be done
is to inherited your Application class from PyramidApplication
insted of the
normal configurator. The new Configurator will be responsible for running
Pyramid’s plugins as well.
For example, we will add SettingsPlugin fro Sapp and RoutingPlugin from Sapp’s Pyramid Plugin.
from qq.plugins.pyramid.application import PyramidApplication
from qq.plugins.pyramid.plugins import RoutingPlugin
from myapp.application.routing import MyappRouting
class MyApp(PyramidApplication):
def create_plugins(self):
self.plugins["settings"] = SettingsPlugin('myapp.application.settings')
self.plugins["routing"] = RoutingPlugin(MyappRouting)
myapp = MyApp()
As you can see, you can use normal plugins along with the Pyramid’s specifyc ones. More about plugins can be found here
4.4.2.2. Implement Startpoint
Second step of implementing Pyramid’s Plugin is to create a startpoint. It needs to be a function which will return an wsgi object, which will be used by uwsgi and paster. In order to do that we need to start the Configurator.
Example:
from myapp import myapp
def wsgifunction(settings): # settings is dict with configuration from .ini file
myapp.start('pyramid')
return myapp.make_wsgi_app()
The settings argument is not used here. It is something that will be passed to the function by the uwsgi or paster, but Sapp Settings does not use it at all.
4.4.2.3. Configuring Paste and Gunicorn
Last file to create is an app.ini. This file is an configuration for paste and gunicorn.
[app:main]
use = call:{PY_URL}:wsgifunction
[server:main]
use = egg:gunicorn#main
host = 0.0.0.0
port = 8000
[pipeline:main]
pipeline =
main
[app:main]
section is here to tell the paste which function to use in order to
create wsgi application.
This section is also used for Pyramid’s settings, but the Quack Quack is using the SettingsPlugin, so we leave this section empty.
[server:main]
sections is here to configure the gunicorn server.
Description for the [pipeline:main]
section can be found here
More info about the Paster .ini file can be found here:
4.4.2.4. Starting development server
In order to start the development server you need to run pserve with a path for
app.ini
file. It is usefull also to add --reload
switch, so the server will
be restarting every time the python files will change.
pserve app.ini --reload
4.4.2.5. Creating Plugins for Pyramid
PyramidApplication
will run start_pyramid(pyramid)
method for all
plugins when running .make_wsgi_app
. Of corse, if the application will not
find the start_pyramid
method, it will not raise any error, because otherwise
the old plugins would be not compatible with the PyramidApplication
. So if you
want to make a Pyramid’s specifyc plugin, you should just add
start_pyramid(pyramid)
method to your normal plugin.
pyramid
in start_pyramid(pyramid)
method is pyramid.config.Configurator
instance.
Implementation of the CsrfPlugin should be a good example:
class BasePyramidPlugin(SettingsBasedPlugin):
def start(self, application: PyramidApplication):
self.settings = self.get_my_settings(application)
class CsrfPlugin(BasePyramidPlugin):
"""
Add csrf mechanism to the pyramid app.
"""
def __init__(self, policy_cls):
self.policy_cls = policy_cls
def start_pyramid(self, pyramid):
pyramid.set_csrf_storage_policy(self.policy_cls())
pyramid.set_default_csrf_options(
require_csrf=True,
token=self.settings["csrf_token_key"],
4.4.3. Routing Wrapper
4.4.3.1. Why we need a router wrapper
qq.plugins.pyramid.routing.Routing
was designed to simplify creating of
routes. In normal Pyramid, the developer needs to configure the route in one
place and the view in another. Also, configuring is made by @view_config
decorators which is not a good way if you want to share some values between
many classes, because you can not use polymorphism. Instead you uneed to copy these
configuration variables across all the views.
Another disadvantage of normal pyramid’s routing is that the linking of the route and the view is made by name which is not very sophisticated and it is very buggable.
4.4.3.2. How to implement Routing
First step is to implement Routing class inherited from
qq.plugins.pyramid.routing.Routing
and make a make(self)
method.
This is our wrapper for normal pyramid routing. It will help us, but if you want
to use the old ways, you are free to do that. pyramid
property from the
Routing
class is a Pyramid Configurator.
The make(self)
should add all the routes, but you can import routes from
another module. Using import system makes this very simple and easy to read, but
please be aware, that you should not import Sapp Configurator
instance, because
it will raise cross import error. Also you should not import the views,
because it may raise the same error as well. You should use only dotted strings.
Example:
from qq.plugins.pyramid.routing import Routing
from myapp.home.routing import home_routing
def not_home_routing(routing):
routing.add('mypet.not_home.views.NotHome', 'not_home', '/not')
class MyappRouting(Routing):
def make(self):
home_routing(self)
not_home_routing(self)
The only method which neededs description is Routing.add
. First argument is
dotted path to the view (or view class if you wish). Second is route
name. Third is the route url. All other args and kwargs will be passed to the
add_route method. In order this route
to work, the Routing wrapper will call the add_view
method. All the kwargs for this method will be taken from the view class.
Example view:
class View(object):
rendered = 'json'
def __init__(self, root_factory, request):
self.root_factory = root_factory
self.request = request
def __call__(self):
return {}
4.4.4. Views
Quack Quack comes with base class for every View.
Main reason to implement an view is to generate response proper response.
The simples way to return the data is to implement .get(self)
method and
return a dict.
from qq.plugins.pyramid.view import View
class Home(View):
renderer = 'json'
def get(self):
return {'hello': 'world'}
The renderer property here is to configure the view, so the framework will know that this view will return json data. More info about the configuration properties can be found here).
If you want to create a view which returns template, you can implement it in this way:
from qq.plugins.pyramid.view import View
class Home(View):
renderer = 'templates/hello.jinja2'
def get(self):
return {'hello': 'world'}
Name o the methods is almost the same as the HTTP methods:
.get
Requests using GET should only retrieve data and should have no other effect.
.post
The POST method requests that the server accept the entity enclosed in the request as a new subordinate of the web resource identified by the URI.
.put
The PUT method requests that the enclosed entity be stored under the supplied URI.
.patch
The PATCH method applies partial modifications to a resource.
.delete
The DELETE method deletes the specified resource.
.options
The OPTIONS method returns the HTTP methods that the server supports for the specified URL.
More info about HTTP methods can be found here
4.4.4.1. RestfulView
RestfulView is a View, with JSON as renderer. So it will be more suitable for RESTful views. Name of methods are the same as in View class.