Views¶
Add a New View¶
Configuration¶
The main configuration of the library is done on the view. By subclassing
CRUDView
for each new view you can create an individual configuration
that turns your model & form into a fully accessible CRUD interface. The
available confiugration parameters are described on the class:
-
class
pyramid_crud.views.
CRUDView
(request)[source]¶ The base class for all views. Subclassing directly from this gets you a new view configuration for a single model & form. If you specify
__abstract__
on it, the class will not be configured at all and you can use it as your own base class.Note
Configuration is done by Pyramid the moment you call
pyramid.config.Configurator.scan()
in a way similar to what thepyramid.view.view_config
decorator does. If you want to completely disable this behavior, set view_configurator_class toNone
. Then no route configuration will be done and you have to set up views and routes yourself. This is an advanced technique not recommended for beginners.The following attributes can be defined to override behavior of the view:
- Form
- Mandatory argument that specifies the form class for which this view should be created. This must be a form as described in Forms.
- url_path
Mandatory arguments if the default view_configurator_class is used. It determines the base path under which this view should be available.
So for example, if this is
/myitems
then the list view will be reached under the/myitems
path whereas the new view will be under/myitems/new
.How and if this parameter is used depends entirely on the implementation of the configurator but it is recommended to keep this parameter for custom implementations as well.
- dbsession
- Return the current SQLAlchemy session. By default this
expects a
dbsession
attribute on therequest
object. It is mandatory that you either attach the attribute using an event or override this attribute (you can use aproperty
if you like).
- list_display
A tuple if items which should be displayed on the list view. By default a single column of the models
__str__
method is used. There are several possibilities of what you might specify here (the options will be tried in this order):A string representing an attribute or callable on the model. If this attribute is callable, it will be called and get no additional arguments (the first argument will already be
self
, the model instance).For example, with a normal field on the model:
class Model(Base): id = Column(Integer, primary_key=True, info={'label': 'ID'}) class View(CRUDView): list_display = ('id',)
In this example there will be a single column in the list view. Its title will be “ID” and its value will be the value of the
id
field in the database.Similarly, with a callable:
class Model(Base): id = Column(Integer, primary_key=True) def id_plus_one(self): return self.id + 1 id_plus_one.info = {'label': 'ID+1'} class View(CRUDView): list_display = ('id_plus_one',)
A generic callable function. This function will be called with a single argument: The instance of the model. For example:
class Model(Base): id = Column(Integer, primary_key=True) def id_plus_one(obj): return obj.id + 1 id_plus_one.info = {'label': 'ID+1'} class View(CRUDView): list_display = (id_plus_one,)
A string representing a method on the view. This will behave in the same way as for the function callable above except that it must be a string. For example:
class Model(Base): id = Column(Integer, primary_key=True) class View(CRUDView): list_display = ('id_plus_one',) def id_plus_one(self, obj): return obj.id + 1 id_plus_one.info = {'label': 'ID+1'}
Some additional notes on the way this attribute behaves:
- Some additional configuration is possible on each attribute, regardless of how it is specified. For information on this see The Info Dictionary.
- A class
columnn-<attr-name>
is placed on each on each of the <th> fields in the column heading to allow application of CSS attributes, e.g. to set the width of a column. - If the attribute
info
cannot be found on the attribute (at the class level, not instance level), default value is determined as the column heading. If name of the column is__str__
then the name of the model class is fetched. If it is directly callable (in case of a generic callable function), then the name of the function is used. In all other cases the provided string is used. To make for a prettier format, it additionally replaces any underscores by spaces and captializes each word.
- list_display_links
Specify which of the displayed columns should be turned into links that open the edit view of that instance. By default, the first column is used.
This should be any kind of iterable, preferrably a tuple or set for performance reasons.
Example:
class MyView(CRUDView): list_display = ('column1', 'column2', 'column3') list_display_links = ('column1', 'column3')
This configuration will turn the columns
column1
andcolumn3
into links.
- actions:
- An optional list of action callables or view method names for the dropdown menu. See Adding Actions to Forms for details on how to use it.
- theme
- A theme is just a collection of template files inside a directory and
this is the name of that directory. The recommended way is to use
asset specification to unambigously identify the package. By default
the bootstrap template is used and so this is set to
pyramid_crud:templates/mako/bootstrap
. If you want to roll your own theme, you can overwrite this. But if you only want to copy a single template and modify it, you should check out Templates.
- template_ext
- Which file extension to use for templates. By default,
Mako templates are used and so the extension is
.mako
but any renderer that is recognized by pramid can be used.
- template_*
You can specify any name here, e.g.
template_list
and theCRUDView.get_template_for()
method will use this when calling it withlist
as the action parameter. This is useful for overwriting specific templates but keeping the default behavior for the rest.Note
The name “ext” for an action is thus not allowed (as
template_ext
is another configuration). Just don’t define an action with that name.This way is also impossible for templates in subdirectories, for example
fieldsets/horizontal.mako
since a slash (“/”) cannot be used on a path. Currently the only way is to overwriteCRUDView.get_template_for()
.
- view_configurator_class
- A class that configures all views and routes for this view class. The
default implementation is
ViewConfigurator
which covers basic route & view configuration. However, if you need more advanced functionalities like, for example, permissions, you can change this parameter. See the documentation onViewConfigurator
for details on how to achieve that.
There are also some attributes which you can access. All of them are available on the instance, but only some are also available on the class (in this case, it is noted on the attribute).
- routes
A dictionary mapping action names to routes. Action names are such as
list
oredit
and they all have unique route names that can be given torequest.route_url
. You can use it like this:url = request.route_url(view.routes["list"])
This will return a URL to the list view.
The routes dictionary is populated by the view_configurator_class.
This can be accessed at the class and instance level.
- request
- The current request, an instance of
pyramid.request.Request
.
View & Route Setup¶
Setting up views and routes is delegated to a special configurator class that creates a route & view for each available view, i.e. list, edit, new and delete. Since you often need to change the routes and views to match your needs, you can subclass this and start overwriting its behavior. The interface is very simple:
Note
There is a slight overhead to configuring views like this because it requires the creation of an additional class. However, approaches like configuring parameters directly on the view are inflexible and setting awkward callables (in theory the most pythonic way) look ugly. Thus, this method is both flexible and easy to read.
-
class
pyramid_crud.views.
ViewConfigurator
(config, view_class)[source]¶ The standard implementation of the view configuration. It performs the most basic configuration of routes and views without any extra functionality.
This is sufficient in many cases, but there are several applications where you might want to completely or partially change this behavior. Any time you want to pass additional arguments to
pyramid.config.Configurator.add_route()
orpyramid.config.Configurator.add_view()
you can just subclass this and override the specific methods.All the public methods must always be implemented according to their documentation or the configuration of views and routes will fail. If you are unsure, you can take a look at the default implementation. It is just a very thin wrapper around the above mentioned methods.
During instantiation the arguments
config
representing an instance ofpyramid.config.Configurator
andview_class
being your subclassed view class are given to the instance and stored under these values as its attributes.From the
view_class
parameter you can access the complete configuration as documented onCRUDView
.config
should then be used to add routes and views and possibly other configuration you might need.
-
ViewConfigurator.
configure_list_view
()[source]¶ Configure the “list” view by setting its route and view. This method must call
add_view
to configure the view andadd_route
to connect a route to it. Afterwards, it must return the name of the configured route that links route and view. This will then be stored in the view’sroute
dictionary under the “list” key.def configure_list_view(self): self.config.add_view('myview-list', renderer='list.mako',) self.config.add_route('myview-list', self.view_class.url_path) return 'myview-list'
This does a few things:
- It sets up the view under the alias
myview-list
with the templatelist.mako
. Note that the default configuration uses a theme and absolute paths while this configures a template that needs to be inmako.directories
. - It connects the alias to the configured route via the url_path configuration parameter (the list view is just the base route in this case, but that is totally up to you).
- It returns this alias from the function so that it can be stored in
the
routes
dictionary on the view.
- It sets up the view under the alias
-
ViewConfigurator.
configure_edit_view
()[source]¶ This method behaves exactly like
ViewConfigurator.configure_list_view()
except it must configure the edit view, i.e. the view for editing existing objects. It must return the name of the route as well that will then be stored under the “edit” key.
-
ViewConfigurator.
configure_new_view
()[source]¶ This method behaves exactly like
ViewConfigurator.configure_list_view()
except it must configure the new view, i.e. the view for adding new objects. It must return the name of the route as well that will then be stored under the “new” key.
There are also some helper methods available.
The Info Dictionary¶
Each object can have an optional info dictionary attached (and in most cases you will want one). The idea is based on the idea of WTForms-Alchemy’s Form Customization and actually just extends it. Several attributes used by this library support inclusion of extra information in this dict. The following options can be set and/or read and some are automatically defined if you do not provide a value. The follwoing values are available:
- label
- This is taken over from WTForms-Alchemy but is used in more places. Instead of being just used as the label on a form, it is also used as a column heading in the list view. Each object should have one, but some functions set it (for example, the column header function associated with list_display provides a default). For specific behavior on this regarding different views you should consult the associated documentation. While you should normally set it, not setting it will invent some hopefully nice-looking strings for the default usage (basically list and edit views).
- description
- Used on form fields to describe a field more in-depth than a label can. This text may be arbitrarily long. It is not displayed on all templates (see Fieldset Templates).
- css_class
- A css class which should be set on this element’s context. Currently this
is only used for the list view where the
th
element gets this class so you can style your table based on individual columns. See the documentation on list_display for more info. - bool
- This value is not always set, but when it is set, it indicates if this item is a boolean type. Currently this is only set for the list headings and there it is unused but can be adapted by custom templates.
- func
- This is only used with actions and defines the callable which executes an
action. It is part of the dict returned by
_all_actions
on the view.
API¶
The classes, methods and attributes described here are normally not used directly by the user of the library and are just here for the sake of completeness.
CRUDView
¶
The following methods refer to specific views:
-
CRUDView.
list
()[source]¶ List all items for a Model. This is the default view that can be overridden by subclasses to change its behavior.
Returns: A dict with a single key items
that is a query which when iterating over yields all items to be listed.
-
CRUDView.
edit
()[source]¶ The default view for editing an item. It loads the configured form and model. In edit mode (i.e. with an already existing object) it requires a matchdict mapping primary key names to their values. This has to be ensured during route configuring by setting the correct pattern. The default implementation takes correctly care of this.
Returns: In case of a GET request a dict with the key form
denoting the configured form instance with data from an optional model loaded and a keyis_new
which is a boolean flag indicating whether the actual action isnew
oredit
(allowing for templates to display “New Item” or “Edit Item”).In case of a POST request, either the same dict is returned or an instance of
HTTPFound
which indicates success in saving the item to the database.Raises: ValueError – In case of an invalid, missing or unmatched action. The most likely reason for this is the missing button of a form, e.g. by the name save
. By default the following actions are supported:save
,save_close
,save_new
and additionally anything that starts withadd_
ordelete_
(these two are for internal form handling and inline deletes/adds).
Addtionally, the following helper methods are used internally during several sections of the library:
-
CRUDView.
redirect
(route_name=None, *args, **kw)[source]¶ Convenience function to create a redirect.
Parameters: route_name – The name of the route for which to create a URL. If this is None
, the current route is used.All additional arguments and keyword arguments are passed to
pyramid.request.Request.route_url()
.Returns: An instance of pyramid.httpexceptions.HTTPFound
suitable to be returned from a view to create a redirect.
-
classmethod
CRUDView.
get_template_for
(action)[source]¶ Return the name of the template to be used. By default this uses the template in the folder
theme
with the nameaction + template_ext
, so for example in the default case for a list view: “pyramid_crud:templates/mako/bootstrap/list.mako”.This method basically just appends the given action to a base path and appends the file extension. As such, it is perfectly fine, to define relative paths here:
view.get_template_for('fieldsets/horizontal')
You can also change single templates by statically defining
action_template
on the view class whereaction
is replaced by a specific action, e.g.list
. So say, for example, you want to only change the default list template and keep the others. In that case, you would specifylist_template = "templates/my_crud_list.mako"
and the list template would be loaded from there (while still loading all other templates from their default location).Parameters: action – The action, e.g. list
oredit
.
-
CRUDView.
_get_request_pks
()[source]¶ Get an ordered dictionary of primary key names matching to their value, fetched from the request’s matchdict (not the model!).
Parameters: names – An iterable of names which are to be fetched from the matchdict. Returns: An OrderedDict
of the givennames
as keys with their corresponding value.Raises: ValueError – When only some primary keys are set (it is allowed to have all or none of them set)
-
CRUDView.
_get_route_pks
(obj)[source]¶ Get a dictionary mapping primary key names to values based on the model (contrary to
_get_request_pks()
which bases them on the request).Parameters: obj – An instance of the model. Returns: A dict with primary key names as keys and their values on the object instance as the values.
-
CRUDView.
_edit_route
(obj)[source]¶ Get a route for the edit action based on an objects primary keys.
Parameters: obj – The instance of a model on which the routes values should be based. Returns: A URL which can be used as the routing URL for redirects or displaying the URL on the page.
ViewConfigurator
¶
In addition to the methods described above, the default implementation has a few helper methods. These are not required in any case since they are only called by the above methods. However, since these methods are used to factor out common tedious work, you might either use or override them and possibly not even touch the default implementations above.
-
ViewConfigurator.
_configure_view
(action, route_action=None, *args, **kw)[source]¶ Configure a view via
pyramid.config.Configurator.add_view()
while passing any additional arguments to it.Parameters: - action – The name of the attribute on the view class that
represents the action. For example, in the default
implementation the
list
action corresponds toCRUDView.list()
. If you rename them, e.g. by naming thelist
action “_my_list”, this would have to be_my_list
regardless of the name of the action. - route_action – An optional parameter that is used as the name
base for the route. If this is missing, it will
take the same value as
action
. In the default implementation it is used to distinguish betweennew
andedit
which use the same action, view and template but different route names.
Overriding this method allows you to change the view configuration for all configured views at once, i.e. you don’t have to change the public methods at all. Just look at their default implementation to see the parameters they use.
- action – The name of the attribute on the view class that
represents the action. For example, in the default
implementation the
-
ViewConfigurator.
_configure_route
(action, suffix, *args, **kw)[source]¶ Set up a route via
pyramid.config.Configurator.add_route()
while passing all addtional arguments through to it.Parameters: - action – The action upon which to base the route name. It must
be the same as
route_action
on_configure_view()
. - suffix – The suffix to be used for the actual path. It is
appended to the
url_path
directly. This may be empty (as is the case for the default list view) but must always be explicitly specified. The result of this will be passed toadd_route
and so may (and often will) include parameters such as/{id}
.
Overriding this method can be done in the same manner as described for
_configure_view()
.Warning
Some methods on the view require primary keys of the object in question in the
matchdict
of the request. To guarantee this, the routes have to be correctly set up, i.e. each route that requires this primary key (or keys, depending on the model) has to have a pattern where each primary key name appears once. The default implementation takes care of this via_get_route_pks()
, but if you change things you have to ensure this yourself.Which methods require which values is documented on the respective views of
CRUDView
.- action – The action upon which to base the route name. It must
be the same as
-
ViewConfigurator.
_get_route_name
(action)[source]¶ Get a name for a route of a specific action. The default implementation provides the fully quallyfied name of the view plus the action, e.g.
mypackage.views.MyView.list
(in this case, the action is “list” for the class “MyView” in the module “mypackage.views”).Note
In theory this implementation is ambigous, because you could very well have two classes with the same name in the same module. However, this would be a very awkward implementation and is not recommended anyway. If you really choose to do such a thing, you should probably find a better way of naming your routes.
-
ViewConfigurator.
_get_route_pks
()[source]¶ Get a string representing all primary keys for a route suitable to be given as suffix to
_configure_route()
. Some examples will probably best describe the default behavior.In the case of a model with a single primary key
id
, the result is the very simple string{id}
. If you add this to a route, the primary key of the object will be in thematchdict
under the keyid
.If you have a model with multiple primary keys, say composite foreign keys, called
model1_id
andmodel2_id
then the result would be{model1_id},{model2_id}
. The order is not important on this one as pyramids routing system will fully take care of it.Note
If you have some kind of setup where one of the primary keys may contain a comma, this implementation is likely to fail and you have to change it. However, in most cases you will not have a comma and this should be fine.