Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Chapter Table of Contents
- web2py: py2manager
- Introduction
- Setup
- Sublime Text
- Version Control
- Database
- Shell
- web2py Admin
- Homework
- URL Routing
- URL Routing
- Homework
- Initial Views
- Profile Page
- Add Projects
- Add Companies
- Homepage
- More Grids
- 8.12 Notes
- Error Handling
- Homework
- web2py: py2manager
- Introduction
- In the last chapter we built several small applications to illustrate the power of web2py. Those applications were meant more for learning. In this chapter we will develop a much larger application: a task manager, similar to FlaskTaskr, called py2manager.
- This application will be developed from the ground up to not only show all that web2py has to offer - but to also dig deeper into modern web development and the Model View Controller development style.
- This application will do the following:
- 1. Users must sign in (and register, if necessary) before hitting the landing page, index.html. 1. Once signed in, users can add new projects. Each project consists of a name, due date, priority, status, and an auto-incremented ID. 1. Users can view all incomplete tasks from the same screen. 1. Users can also delete tasks and mark tasks as complete. If a user deletes a task, it will also be deleted from the database.
- Up to this point, you have developed a number of different applications using the Model View Controller (MVC) architecture pattern:
- 1. Model: data warehouse (database) 2. View: data output (templates, JavaScript, CSS, HTML, etc.) 3. Controller: link between the user and the application
- Again, a user sends a request to a web server. The server, in turn, passes that request to the controller. Using the established workflow, the controller then performs an action, such as querying or modifying the database (model). Once the data is found or updated, the controller then passes the results back to the views, which is seen by the user (response).
- Most modern web frameworks utilize MVC-style architecture, offering similar components. But each framework implements the various components slightly different, due to the choices made by the developers of the framework. Learning about such differences is vital for sound development.
- We'll look at MVC in terms of web2py as we develop py2manager.
- Setup
- Before we begin, let's setup a virtualenv and a new app:
- 1. Navigate to your "realpython/web2py" directory
- 2. Install a new virtualenv:
- virtualenv --no-site-packages py2manager
- 1. CD into the new directory, and then activate the virtual environment:
- Unix:
- source bin/activate
- Windows:
- scripts\activate
- 1. Download the source code from the web2py website and place it into the "py2manager" directory. Unzip the file, placing all files and folders into the "py2manager" directory.
- 1. Create a new app from your terminal:
- python web2py.py -S py2manager
- 1. Exit the Shell.
- 2. Navigate to the "Applications" directory, then into the "py2manager" directory. This directory holds all of your application's files.
- Sublime Text
- Before moving on, we're going to start using a more advanced text editor called Sublime Text to help keep our project organized and speed up development. The internal web2py IDE is great for small applications, but you'll want to use either an adavanced text editor or an IDE for larger applications.
- You can download Sublime Text 2 here. Once installed, you need to load your development project:
- 1. "Project" => "Add folder to project" 2. Navigate to your "realpython/py2manager/applications/py2manager" directory (where your application data is held). 3. Select the directory and then click Open.
- You should now have the entire application structure (files and folders) in Sublime. Take a look around. Open the Model, View, and Controller folders. Simply double-click to open a particular file in the editor window. Files appear as tabs on the top of the editor window, allowing you to move between files quickly.
- image
- Version Control
- It's common practice to put your application under version control before development. We already addressed the need for this in Chapter 6, so skip back to that chapter for the details, if needed.
- 1. Start by downloading Git, if you don't already have it.
- 2. If you've never installed Git before you need to set your global first name, last name, and email address. Open the terminal in Unix or the Git Bash Shell in Windows (Start > All Programs > Git > Git Bash), then enter the following commands:
- git config --global user.name "FIRST_NAME LAST_NAME"
- git config --global user.email "[email protected]"
- 1. Sign up for Github to host your Git repository (which is just a secure location to store your files), and setup a new repository.
- 2. Back in your terminal, navigate to your project directory, then run the following commands:
- git init
- touch README
- git add *
- git commit -m "My initial commit message"
- git remote add origin [email protected]:Your-Username/awesomeProject.git
- git push origin master
- 1. This creates the necessary files and pushes them to the remote repository on Github.
- 1. You'll want to PUSH after each lesson:
- git add *
- git commit -m 'Text goes here'
- git push origin master
- Let's begin developing our application.
- Database
- As you saw in the previous chapter, web2py uses an API called a Database Abstraction Layer (DAL) to map Python objects to database objects. Like an ORM, a DAL hides the complexity of the underlying SQL code. The major difference between an ORM and a DAL, is that a DAL operates at a lower level.[md.1] In other words, its syntax is somewhat closer to SQL. If you have experience with SQL, you may find DAL easier to work with than an ORM. If not, learning the syntax is no more difficult than an ORM:
- ORM:
- class User(db.Model):
- __tablename__ = 'users'
- name = db.Column(db.String, unique=True, nullable=False)
- email = db.Column(db.String, unique=True, nullable=False)
- password = db.Column(db.String, nullable=False)
- DAL:
- db.define_table('users',
- Field('name', 'string', unique=True, notnull=True),
- Field('email', 'string', unique=True, notnull=True),
- Field('password', 'string', 'password', readable=False, label='Password'))
- The above examples create the exact same "users" table. ORMs generally use classes to declare tables, while the web2py DAL uses functions. Both are portable among many different relational database engines (database agnostic). Meaning you can switch your engine without having to re-write the code within your Model. web2py is integrated with a number of popular databases, including SQLite, PostgreSQL, MySQL, MSSQL, FireBird, Oracle, MongoDB, among others.
- Shell
- If you prefer the command line, you can work directly from the web2py Shell. The following is a quick, unrelated example:
- In your terminal navigate to the project root directory (realpython/web2py/py2manager), then run the following command:
- python web2py.py --shell=py2manager
- Run the following DAL commands: [md.2]
- >>> db = DAL('sqlite://storage.sqlite',pool_size=1,check_reserved=['all'])
- >>> db.define_table('special_users', Field('name'), Field('email'))
- <Table special_users (id,name,email)>
- >>> db.special_users.insert(id=1, name="Alex", email="[email protected]")
- 1L
- >>> db.special_users.bulk_insert([{'name':'Alan', 'email':'[email protected]'},
- {'name':'John', 'email':'[email protected]'}, {'name':'Tim', 'email':'[email protected]'}])
- [2L, 3L, 4L]
- >>> db.commit()
- >>> for row in db().select(db.special_users.ALL):
- ... print row.name
- ...
- Alex
- Alan
- John
- Tim
- >>> for row in db().select(db.special_users.ALL):
- ... print row
- ...
- <Row {'name': 'Alex', 'email': '[email protected]', 'id': 1}>
- <Row {'name': 'Alan', 'email': '[email protected]', 'id': 2}>
- <Row {'name': 'John', 'email': '[email protected]', 'id': 3}>
- <Row {'name': 'Tim', 'email': '[email protected]', 'id': 4}>
- >>> db.special_users.drop()
- >>> exit()
- 
- web2py Admin
- Now, as I mentioned in the last chapter, everything has a default. These are the default values for each table field:
- Field(name, 'string', length=None, default=None,
- required=False, requires='<default>',
- ondelete='CASCADE', notnull=False, unique=False,
- uploadfield=True, widget=None, label=None, comment=None,
- writable=True, readable=True, update=None, authorize=None,
- autodelete=False, represent=None, compute=None,
- uploadfolder=os.path.join(request.folder,'uploads'),
- uploadseparate=None,uploadfs=None)
- So, the field, Field('company_name'), would by default be a string value, not required (notnull=False), and does not have to be unique (unique=False). Keep this in mind when you are creating your tables.
- Let's create the model for our application.
- 1. Create a new file to define your database schema, in Sublime within Models, called db_tasks.py. Then just below the comment box that states, "Define your tables below (or better in another model file) for example," add the following code:
- db.define_table('company',
- Field('company_name', notnull=True, unique=True),
- Field('email'),
- Field('phone', notnull=True),
- Field('url'),
- format = '%(company_name)s')
- db.company.email.requires=IS_EMAIL()
- db.company.url.requires=IS_EMPTY_OR(IS_URL())
- db.define_table('project',
- Field('name', notnull=True),
- Field('employee_name', db.auth_user, default=auth.user_id, writable=False),
- Field('company_name', 'reference company', notnull=True),
- Field('description', 'text', notnull=True),
- Field('start_date', 'date', notnull=True),
- Field('due_date', 'date', notnull=True),
- Field('completed', 'boolean', notnull=True),
- format = '%(company_name)s')
- We defined a two tables tables: "company" and "project". You can see the foreign key in the project "table", reference client. The "auth_user" table is an auto-generated table, among others. Also, the "employee_name" field in the "project" table references the logged in user. So when a user posts a new project, their user information will automatically be added to the database. Save the file.
- Fire up web2py in your terminal: python web2py.py -a 'your password' -i 127.0.0.1 -p 8000
- Navigate to the Edit page and click the "database administration" button to execute the DAL commands.
- Take a look at the sql.log file within the databases directory in Sublime to verify exactly which tables and fields were created. You can also read the documentation on all the auto-generated tables in the web2py official documentation.
- Notice the format attribute. All references are linked to the Primary Key of the associated table, which is the auto-generated ID (check the sql.log). By using the format attribute references will not show up by the id - but by the preferred field.
- Register yourself as a new user, then setup a new a company, a project, and an employee:
- Navigate to the following url: http://localhost:8000/py2manager/default/user/login
- Click register, then enter your information.
- Navigate to: http://localhost:8000/py2manager/appadmin/index
- Setup a dummy company and project.
- Remember => web2py addresses a number of potential security flaws automatically. One of them is session management: web2py provides a built-in mechanism for administrator authentication, and it manages sessions independently for each application. The administrative interface also forces the use of secure session cookies when the client is not "localhost".
- One less thing you have to worry about. For more information, please check out the web2py documentation.
- Homework
- Download the web2py cheatsheet. Paste it on your wall. Frame it if you want. Read it too.
- URL Routing
- Controllers describe the application logic and workflow in order to link the user with the application. More precisely, the controller controls the requests made by the users, obtains and organizes the desired information, and then responds back to the user via views and templates. For example, go to the Real Python website and log in. When you clicked the "Log In" button after you entered your credentials, a POST request was sent to the controller. The controller then took that information and compared it with the users in the MySQL database. Once your user credentials were found, this information was sent back to the controller. Then the controller redirected you to the appropriate view.
- Web frameworks simplify this process significantly.
- URL Routing
- web2py provides a simple means of matching URLs with views.[md.3] In other words, when the controller provides you with the appropriate view, there is an URL associated with that view, which can be customized.
- Let's look at an example:
- def index():
- return dict(message="Hello!")
- This is just a simple function used to output the string "Hello!" to the screen. The application name is "hello"; the controller used for this function is default.py; and the function name is "index".
- In this case the generated URL will be:
- http://www.yoursite.com/hello/default/index.html
- This is also the default URL routing method:
- http://www.yousite.com/application_name/controller_name/function_name.html
- You can customize the URL routing methods in the routes.example.py file, which is found in the "web2py" root directory ("realpython/web2py/py2manager"). Just rename it to routes.py. For example, if you wanted to remove the controller_name from the url, add the following code to the routes.py file:
- routers = dict(
- BASE = dict(default_application='py2manager'),
- )
- Go ahead and make those changes.
- Test this out. Restart the server. Navigate to the login page again: http://localhost:8000/py2manager/user/login
- For more information on URL routing, please see the official web2py documentation.
- Let's setup the logic and URL routing in the py2manager app. Add the following code to default.py:
- @auth.requires_login()
- def index():
- project_form = SQLFORM(db.project).process()
- projects = db(db.project).select()
- users = db(db.auth_user).select()
- companies = db(db.company).select()
- return locals()
- Here we displayed the data found in the projects, users, and companies tables, as well as added a form for adding projects.
- Most of the functionality is now in place for a basic application. We just need to update the views, organize the index.html page, and update the update the layout and styles. Before moving on, push the changes to Github.
- Homework
- Please read more about the basic Git commands here.
- Initial Views
- Views describe how the response should be translated to to the user using mostly a combination of HTML, JavaScript, and CSS.
- Some major components of the views include:
- Template Engine: Template engines are used for embedding Python into standard HTML. web2py uses a slightly modified Python syntax to make the code more readable. You can also define control statements such as for and while loops and if statements.
- For example:
- <html>
- <body>
- {{numbers = [1, 2, 3]}}
- <ul>
- {{for n in nums:}}<li>{{=i}}</li>{{pass}}
- </ul>
- </body>
- </html>
- Template Composition: web2py can extend and include a set of sub templates. For example, you could have the main page, index.html, that extends default.html. Meanwhile, default.html could include two sub templates, header.html and footer.html:
- - Main *index.html* page:
- {{extend 'default.html'}}
- <h1>This is the index.html page</h1>
- - The extended layout file, *default.html* must include an {{include}} directive, which embeds the HTML from *index.html*:
- <html>
- <head>
- {{extend 'header.html'}}
- </head>
- <body>
- {{include}}
- </body>
- {{extend 'footer.html'}}
- </html>
- jQuery libraries: web2py includes a number of jQuery and JavaScript libraries - a number of which are pre-configured. Refer to the web2py documentation for more information on jQuery, JavaScript, and other components of the views.
- Let's build the templates and views for py2manager.
- 1. index.html:
- {{extend 'layout.html'}}
- <h2>Welcome to py2manager</h2>
- <br/>
- {{=(project_form)}}
- <br/>
- <h3> All Open Projects </h3>
- <ul>{{for project in projects:}}
- <li>
- {{=(project.name)}}
- </li>
- {{pass}}
- </ul>
- This file has a form at the top to add new projects to the database. It also lists out all open projects using a for loop. You can view the results here: [http://localhost:8000/py2manager/index](http://localhost:8000/py2manager/index).
- 2. user.html:
- Open up this file. This view was created automatically to make the development process easier and quicker. If you go back to the *default.py* file, you can see a description of the main functionalities of the user function:
- """
- exposes:
- http://..../[app]/default/user/login
- http://..../[app]/default/user/logout
- http://..../[app]/default/user/register
- http://..../[app]/default/user/profile
- http://..../[app]/default/user/retrieve_password
- http://..../[app]/default/user/change_password
- use @auth.requires_login()
- @auth.requires_membership('group name')
- @auth.requires_permission('read','table name',record_id)
- to decorate functions that need access control
- """
- 3. Layout
- Let's edit the main layout to replace the generic template. Start with *models/menu.py*. Update the following code:
- response.logo = A(B('py',SPAN(2),'manager'),XML('™ '),
- _class="brand")
- response.title = "py2manager"
- response.subtitle = T('just another project manager')
- Then update the application menu:
- response.menu = [(T('Home'), False, URL('default', 'index'), []),
- (T('Add Project'), False, URL('default', 'add'), []),
- (T('Add Company'), False, URL('default', 'company'), []),
- (T('Employees'), False, URL('default', 'employee'), [])]
- DEVELOPMENT_MENU = False
- Take a look at your changes. Remember to PUSH to Github:
- git add *
- git commit -m 'Added a big photo to index.html'
- git push origin master
- Now that we've gone over the Model View Controller architecture in detail, let's now focus on the main functions of the application.
- Profile Page
- Remember the auto-generated "auth_user" table? Take a look at the sql.log for a quick reminder. Again, the auth_user table is part of a larger set of auto-generated tables aptly called the Auth tables.
- It's easy to add fields to any of the Auth tables. Open up db.py in Sublime and place the following code after auth = Auth(db) and before auth.define_tables):
- auth.settings.extra_fields['auth_user']= [
- Field('address'),
- Field('city'),
- Field('zip'),
- Field('image', 'upload')]
- Save the file. Navigate to http://localhost:8000/py2manager/index and login if needed. Once logged in, you can see your name in the upper right-hand corner. Click the drop down arrow, then select "Profile". You should see the new fields. Go ahead and update the new fields and upload an image. Then click Save Profile. Nice, right?
- image
- Add Projects
- To clean up the homepage, let's move the form to add new projects to a separate page. Open your default.py file, and add a new view:
- @auth.requires_login()
- def add():
- project_form = SQLFORM(db.project).process()
- return dict(project_form=project_form)
- Then update the index() view:
- @auth.requires_login()
- def index():
- projects = db(db.project).select()
- users = db(db.auth_user).select()
- companies = db(db.company).select()
- return locals()
- Add a new template in the default directory called add.html:
- {{extend 'layout.html'}}
- <h2>Add a new project:</h2>
- <br/>
- {{=project_form.custom.begin}}
- <strong>Project name</strong><br/>{{=project_form.custom.widget.name}}<br/>
- <strong>Company name</strong><br/>{{=project_form.custom.widget.company_name}}<br/>
- <strong>Description</strong><br/>{{=project_form.custom.widget.description}}<br/>
- <strong>Start Date</strong><br/>{{=project_form.custom.widget.start_date}}<br/>
- <strong>Due Date<strong><br/>{{=project_form.custom.widget.due_date}}<br/>
- {{=project_form.custom.submit}}
- {{=project_form.custom.end}}
- In the controller, we used web2py's SQLFORM to generate a form automatically from the database. We then customized the look of the form using the following syntax: form.custom.widget[fieldname].
- Push to Github.
- Add Companies
- First, we need to add a form for adding new companies, which follows almost a nearly identical pattern as adding a form for projects. Try working on it on your own before looking at the code.
- Default.py
- @auth.requires_login()
- def company():
- company_form = SQLFORM(db.company).process()
- return dict(company_form=company_form)
- Add a new template in the default directory called company.html:
- {{extend 'layout.html'}}
- <h2>Add a new company:</h2>
- <br/>
- {{=company_form.custom.begin}}
- <strong>Company Name</strong><br/>{{=company_form.custom.widget.company_name}}<br/>
- <strong>Email</strong><br/>{{=company_form.custom.widget.email}}<br/>
- <strong>Phone</strong><br/>{{=company_form.custom.widget.phone}}<br/>
- <strong>URL</strong><br/>{{=company_form.custom.widget.url}}<br/>
- {{=company_form.custom.submit}}
- {{=company_form.custom.end}}
- Push to Github.
- Homepage
- Now, let's finish organizing the homepage to display all projects. We'll be using the grid to display all projects. Essentially, the grid is a high-level table that creates complex CRUD controls. It provides pagination, the ability to browse, search, sort, create, update and delete records from a single table.
- Let's look at a quick example.
- 1. Update the index() function in default.py:
- @auth.requires_login()
- def index():
- response.flash = T('Welcome!')
- grid = SQLFORM.grid(db.project)
- return locals()
- `return locals()` is used to return a dictionary to the view, containing all the variables. It's equivalent to `return dict(grid=grid)`, in the above example. We also added a flash greeting.
- 2. Update the index.html view:
- {{extend 'layout.html'}}
- <h2>All projects:</h2>
- <br/>
- {{=grid}}
- 3. Navigate to http://127.0.0.1:8000/py2manager to view the new layout. Play around with it. Add some more projects. Download them in CSV. Notice how you can sort specific fields in ascending or descending order by clicing on the header links. This is the generic grid. Let's customize it to fit our needs.
- 4. Append the following code to the bottom of db.py:
- db.project.start_date.requires = IS_DATE(format=T('%m-%d-%Y'),
- error_message='Must be MM-DD-YYYY!')
- db.project.due_date.requires = IS_DATE(format=T('%m-%d-%Y'),
- error_message='Must be MM-DD-YYYY!')
- This changes the date format from YYYY-MM-DD to MM-DD-YYYY. What happens if you use a lowercase `y` instead?
- Test this out by adding a new project: [http://127.0.0.1:8000/add](http://127.0.0.1:8000/add). Use the built-in AJAX calendar. Oops. That's still inputing dates the old way. Let's fix that.
- 1. Within the views folder, open web2py_ajax.html and make the following changes:
- Change:
- var w2p_ajax_date_format = "{{=T('%Y-%m-%d')}}";
- var w2p_ajax_datetime_format = "{{=T('%Y-%m-%d %H:%M:%S')}}";
- To:
- var w2p_ajax_date_format = "{{=T('%m-%d-%Y')}}";
- var w2p_ajax_datetime_format = "{{=T('%m-%d-%Y %H:%M:%S')}}";
- 1. Now let's update the grid in the index() function:
- grid = SQLFORM.grid(db.project, create=False, fields=[db.project.name, db.project.employee_name, db.project.company_name, db.project.start_date, db.project.due_date, db.project.completed], deletable=False, maxtextlength=50)
- What does this do? Take a look at the documentation [here](http://web2py.com/books/default/chapter/29/07#SQLFORM.grid-and-SQLFORM.smartgrid). It's all self-explanatory. Compare the before and after for additional help.
- More Grids
- First, let's add a grid to the company view.
- 1. default.py:
- @auth.requires_login()
- def company():
- company_form = SQLFORM(db.company).process()
- grid = SQLFORM.grid(db.company, create=False, deletable=False, editable=False, maxtextlength=50, orderby=db.company.company_name)
- return locals()
- 2. company.html:
- {{extend 'layout.html'}}
- <h2>Add a new company:</h2>
- <br/>
- {{=company_form.custom.begin}}
- <strong>Company Name</strong><br/>{{=company_form.custom.widget.company_name}}<br/>
- <strong>Email</strong><br/>{{=company_form.custom.widget.email}}<br/>
- <strong>Phone</strong><br/>{{=company_form.custom.widget.phone}}<br/>
- {{=company_form.custom.submit}}
- {{=company_form.custom.end}}
- <br/>
- <br/>
- <h2>All companies:</h2>
- <br/>
- {{=grid}}
- Next, let's create the employee view:
- 1. default.py:
- @auth.requires_login()
- def employee():
- employee_form = SQLFORM(db.auth_user).process()
- grid = SQLFORM.grid(db.auth_user, create=False, fields=[db.auth_user.first_name, db.auth_user.last_name, db.auth_user.email], deletable=False, editable=False, maxtextlength=50)
- return locals()
- 1. employee.html:
- {{extend 'layout.html'}}
- <h2>All employees:</h2>
- <br/>
- {{=grid}}
- Test both of the new views out, and then PUSH to Github.
- 8.12 Notes
- Next, let's add the ability to add notes to each project.
- 1. Add a new table to the database to db_task.py:
- db.define_table('note',
- Field('post_id', 'reference project', writable=False),
- Field('post', 'text'),
- Field('created_on', 'datetime', default=request.now, writable=False),
- Field('created_by', db.auth_user, default=auth.user_id, writable=False))
- 2. Update the project() and index() functions in the controller:
- @auth.requires_login()
- def project():
- project = db.project(request.args(0))
- db.note.post_id.default = project.id
- form = SQLFORM(db.note, fields = ['post'])
- all_notes = db(db.note.post_id==project.id).select()
- return locals()
- @auth.requires_login()
- def index():
- response.flash = T('Welcome!')
- notes = [lambda project: A('Notes',_href=URL("default","project",args=[project.id]))]
- grid = SQLFORM.grid(db.project, create=False, links=notes, fields=[db.project.name, db.project.employee_name, db.project.company_name, db.project.start_date, db.project.due_date, db.project.completed], deletable=False, maxtextlength=50)
- return locals()
- 3. project.html:
- {{extend 'layout.html'}}
- <h2>Project Details</h2>
- <br/>
- <p>
- <strong>Project Name: </strong>{{=project.name}}<br/>
- <strong>Due Date: </strong>{{=project.due_date.strftime('%m-%d-%Y')}}{{pass}}<br/>
- <strong>Description: </strong>{{=project.description}}{{pass}}<br/>
- </p>
- <h4>Project Notes</h4>
- {{for note in all_notes:}}
- <ul>
- <li>{{=db.auth_user[note.created_by].first_name}} on {{=note.created_on.strftime('%m-%d-%Y')}}
- - {{=note.post}}</li>
- </ul>
- {{pass}}
- <h4>Add a note</h4>
- {{=form}}</br>
- Take a look. Add some notes. PUSH to Github.
- 
- 4. Finally, let's update the index() function to update these links:
- 
- @auth.requires_login()
- def index():
- response.flash = T('Welcome!')
- notes = [lambda project: A("View",_href=URL("default","project",args=[project.id]))]
- grid = SQLFORM.grid(db.project, create=False, ui="jquery-ui", links=notes, fields=[db.project.name, db.project.employee_name, db.project.company_name, db.project.start_date, db.project.due_date, db.project.completed], details=False, deletable=False, maxtextlength=50)
- return locals()
- Error Handling
- web2py handles errors much different than other frameworks. Tickets are automatically logged, and web2py does not differentiate between the development and production modes.
- Have you seen an error yet? Remove the closing paranthesis from the statement in the index() function: response.flash = T('Welcome!'. Now naviagate to the homepage. You should see that a ticket number was logged. When you click on the ticket number, you get the specific details regarding the error.
- You can also view all tickets here: http://localhost:8000/admin/errors/py2manager
- You do not want users seeing errors, so add the following code to the routes.py file:
- routes_onerror = [
- ('*/*', '/py2manager/static/error.html')
- ]
- Then add the edit.html file:
- <h2>This is an error. We are working on fixing it.</h2>
- Refresh the homepage to see the new error message. Now errors are still logged, but end users won't see them. Correct the error. PUSH the new code to GitHub.
- Homework
- Please read the web2py documentation regarding error handling
- http://latticeqcd.org/book_herman/default/chapter/10/08#web2py-Admin
Advertisement
Add Comment
Please, Sign In to add comment