A concept is a way to organize code. Technically it’s a package that bundles the code needed to view, create, modify and delete (or CRUD: create, read, update, delete) a thing or entity in your application’s domain. Most applications will consist of multiple concepts. Typical concepts would be user, profile, comment, item, document or department.
The thing we want to implement here is a blog post. We use ekklesia-portal as target project here but this also works for other Python Ekklesia applications with a Web UI.
Concepts use the following objects passed in from the outside:
- Request: A HTTP request object, provided by WebOb and extended by Morepath’s Request. Gives access to a database session, POST data, a browser session and application settings, for example.
- Model: Typically, this is an object from a database or a collection of them. But can be of any class that is used to carry around information belonging to a concept.
- Cell: Renders a HTML view of a concept by using data from a model object.
- Template: Cells usually produce HTML output by using a Pyjade Template. Templates use code from their associated cell and model fields to display stuff.
- Path: Maps URL pattern to a model. It’s easy to link to model instances from cells by using the
request.linkmethod. (-> Morepath: Paths and Linking)
- View: Contains code to prepare and render a HTTP response. In most cases, HTML views should delegate the actual rendering to a cell. (-> Morepath: Views)
- Form: Renders a HTML form based on a schema, captures the HTTP response and passes the input to the schema for validation. (-> Deform: Basic Usage)
- Schema: Defines properties that are rendered by a form, validates incoming data that should be written to a model object.
Generate a Concept¶
The Command ekklesia-generate-concept generates a working concept implementation together with a test. It contains views for creating, editing and displaying instances of the concept.
Let’s generate our Comment concept:
$ ekklesia-generate-concept blog_post generated concept /home/ts/git/ekklesia-portal/src/ekklesia_portal/concepts/blog_post, tests are located at tests/concepts/blog_post
The generate source tree looks like this:
$ tree /home/ts/git/ekklesia-portal/src/ekklesia_portal/concepts/blog_post /home/ts/git/ekklesia-portal/src/ekklesia_portal/concepts/blog_post ├── blog_post_cells.py ├── blog_post_contracts.py ├── blog_post_helper.py ├── blog_posts.py ├── blog_post_views.py ├── __init__.py └── templates ├── blog_post.j2.jade ├── blog_posts.j2.jade ├── edit_blog_post.j2.jade └── new_blog_post.j2.jade
blog_post_views.py: path and view functions
- path blog_posts: handles listing blog posts and creating new ones
- path blog_post: handles viewing a blog post and editing it
- view index: list blog posts
- view new: show form for new blog post
- view create: handle POST request from the new blog post form
- view edit: show form for editing an existing blog post
- view update: handle POST request from the edit blog post form
blog_post_contracts.py: bundles blog post schema and form
blog_posts.py: collection model used by the blog posts path
blog_post_helper.py: utilities that can be used in cells and from other concepts
Tests live outside of the application package:
$ tree /home/ts/git/ekklesia-portal/tests/concepts/blog_post /home/ts/git/ekklesia-portal/tests/concepts/blog_post ├── __init__.py └── test_blog_posts.py
- unit test a cell: given a model instance, when using an option or not, then produces the expected HTML output
- functional testing of views: fill out forms, submit and check output with WebTest
- implement cell properties that can be used in a template
- extend the schema and set custom widgets in blog_post_contracts
- put more path and view functions in blog_post_views
- (a bit more advanced) refactor templates with fragments