/docs/source/web/howtos.rst
https://bitbucket.org/prologic/circuits/ · ReStructuredText · 407 lines · 272 code · 135 blank · 0 comment · 0 complexity · 1c9fa668d12e82ec6dfd6a9dec3ec11d MD5 · raw file
- .. module:: circuits
- How To Guides
- =============
- These "How To" guides will steer you in the right direction for common
- aspects of modern web applications and website design.
- How Do I: Use a Templating Engine
- ---------------------------------
- circuits.web tries to stay out of your way as much as possible and doesn't
- impose any restrictions on what external libraries and tools you can use
- throughout your web application or website. As such you can use any template
- language/engine you wish.
- Example: Using Mako
- ...................
- This basic example of using the Mako Templating Language.
- First a TemplateLookup instance is created. Finally a function called
- ``render(name, **d)`` is created that is used by Request Handlers to
- render a given template and apply data to it.
- Here is the basic example:
- .. code-block:: python
- :linenos:
-
- #!/usr/bin/env python
-
- import os
-
- import mako
- from mako.lookup import TemplateLookup
-
-
- from circuits.web import Server, Controller
-
-
- templates = TemplateLookup(
- directories=[os.path.join(os.path.dirname(__file__), "tpl")],
- module_directory="/tmp",
- output_encoding="utf-8"
- )
-
-
- def render(name, **d): #**
- try:
- return templates.get_template(name).render(**d) #**
- except:
- return mako.exceptions.html_error_template().render()
-
-
- class Root(Controller):
-
- def index(self):
- return render("index.html")
-
- def submit(self, firstName, lastName):
- msg = "Thank you %s %s" % (firstName, lastName)
- return render("index.html", message=msg)
-
-
- (Server(8000) + Root()).run()
- Other Examples
- ..............
- Other Templating engines will be quite similar to integrate.
- How Do I: Integrate with a Database
- -----------------------------------
- .. warning:: Using databases in an asynchronous framework is problematic
- because most database implementations don't support asynchronous
- I/O operations.
- Generally the solution is to use threading to hand off
- database operations to a separate thread.
- Here are some ways to help integrate databases into your application:
- 1. Ensure your queries are optimized and do not block
- for extensive periods of time.
- 2. Use a library like `SQLAlchemy <http://www.sqlalchemy.org/>`_
- that supports multi-threaded database operations
- to help prevent your circuits.web web application
- from blocking.
- 3. *Optionally* take advantage of the :class:`~circuits.Worker`
- component to fire :class:`~circuits.task` events wrapping
- database calls in a thread or process pool. You can then use
- the :meth:`~circuits.Component.call` and :meth:`~.circuits.Component.wait`
- synchronization primitives to help with the control flow of
- your requests and responses.
- Another way you can help improve performance
- is by load balancing across multiple backends
- of your web application. Using things like
- `haproxy <http://haproxy.1wt.eu/>`_ or
- `nginx <http://nginx.org/en/>`_ for load balancing
- can really help.
- How Do I: Use WebSockets
- ------------------------
- Since the :class:`~circuits.web.websockets.WebSocketDispatcher`
- id a circuits.web "dispatcher" it's quite easy to
- integrate into your web application. Here's a simple
- trivial example:
- .. code-block:: python
- :linenos:
-
- #!/usr/bin/env python
-
- from circuits.net.events import write
- from circuits import Component, Debugger
- from circuits.web.dispatchers import WebSockets
- from circuits.web import Controller, Logger, Server, Static
-
-
- class Echo(Component):
-
- channel = "wsserver"
-
- def read(self, sock, data):
- self.fireEvent(write(sock, "Received: " + data))
-
-
- class Root(Controller):
-
- def index(self):
- return "Hello World!"
-
-
- app = Server(("0.0.0.0", 8000))
- Debugger().register(app)
- Static().register(app)
- Echo().register(app)
- Root().register(app)
- Logger().register(app)
- WebSockets("/websocket").register(app)
- app.run()
- See the `circuits.web examples <https://bitbucket.org/circuits/circuits/src/tip/examples/web>`_.
- How do I: Build a Simple Form
- -----------------------------
- circuits.web parses all POST data as a request comes through and creates a
- dictionary of kwargs (Keyword Arguments) that are passed to Request Handlers.
- Here is a simple example of handling form data:
- .. code-block:: python
- :linenos:
-
- #!/usr/bin/env python
-
- from circuits.web import Server, Controller
-
-
- class Root(Controller):
-
- html = """\
- <html>
- <head>
- <title>Basic Form Handling</title>
- </head>
- <body>
- <h1>Basic Form Handling</h1>
- <p>
- Example of using
- <a href="http://code.google.com/p/circuits/">circuits</a> and it's
- <b>Web Components</b> to build a simple web application that handles
- some basic form data.
- </p>
- <form action="submit" method="POST">
- <table border="0" rules="none">
- <tr>
- <td>First Name:</td>
- <td><input type="text" name="firstName"></td>
- </tr>
- <tr>
- <td>Last Name:</td>
- <td><input type="text" name="lastName"></td>
- </tr>
- <tr>
- <td colspan=2" align="center">
- <input type="submit" value="Submit">
- </td>
- </tr>
- </table>
- </form>
- </body>
- </html>"""
-
-
- def index(self):
- return self.html
-
- def submit(self, firstName, lastName):
- return "Hello %s %s" % (firstName, lastName)
-
-
- (Server(8000) + Root()).run(
- How Do I: Upload a File
- -----------------------
- You can easily handle File Uploads as well using the same techniques as above.
- Basically the "name" you give your <input> tag of type="file" will get passed
- as the Keyword Argument to your Request Handler. It has the following two
- attributes::
-
- .filename - The name of the uploaded file.
- .value - The contents of the uploaded file.
- Here's the code!
- .. code-block:: python
- :linenos:
-
- #!/usr/bin/env python
-
- from circuits.web import Server, Controller
-
-
- UPLOAD_FORM = """
- <html>
- <head>
- <title>Upload Form</title>
- </head>
- <body>
- <h1>Upload Form</h1>
- <form method="POST" action="/" enctype="multipart/form-data">
- Description: <input type="text" name="desc"><br>
- <input type="file" name="file">
- <input type="submit" value="Submit">
- </form>
- </body>
- </html>
- """
-
- UPLOADED_FILE = """
- <html>
- <head>
- <title>Uploaded File</title>
- </head>
- <body>
- <h1>Uploaded File</h1>
- <p>
- Filename: %s<br>
- Description: %s
- </p>
- <p><b>File Contents:</b></p>
- <pre>
- %s
- </pre>
- </body>
- </html>
- """
-
-
- class Root(Controller):
- def index(self, file=None, desc=""):
- if file is None:
- return UPLOAD_FORM
- else:
- filename = file.filename
- return UPLOADED_FILE % (file.filename, desc, file.value)
-
-
- (Server(8000) + Root()).run()
- circuits.web automatically handles form and file uploads and gives you access
- to the uploaded file via arguments to the request handler after they've been
- processed by the dispatcher.
- How Do I: Integrate with WSGI Applications
- ------------------------------------------
- Integrating with other WSGI Applications is
- quite easy to do. Simply add in an instance
- of the :class:`~circuits.web.wsgi.Gateway`
- component into your circuits.web application.
- Example:
- .. code-block:: python
- :linenos:
-
- #!/usr/bin/env python
-
- from circuits.web.wsgi import Gateway
- from circuits.web import Controller, Server
-
-
- def foo(environ, start_response):
- start_response("200 OK", [("Content-Type", "text/plain")])
- return ["Foo!"]
-
-
- class Root(Controller):
- """App Rot"""
-
- def index(self):
- return "Hello World!"
-
-
- app = Server(("0.0.0.0", 10000))
- Root().register(app)
- Gateway({"/foo": foo}).register(app)
- app.run()
- The ``apps`` argument of the :class:`~circuits.web.wsgi.Gateway`
- component takes a key/value pair of ``path -> callable``
- (*a Python dictionary*) that maps each URI to a given
- WSGI callable.
- How Do I: Deploy with Apache and mod_wsgi
- -----------------------------------------
- Here's how to deploy your new Circuits powered Web Application on Apache
- using mod_wsgi.
- Let's say you have a Web Hosting account with some provider.
- - Your Username is: "joblogs"
- - Your URL is: http://example.com/~joeblogs/
- - Your Docroot is: /home/joeblogs/www/
- Configuring Apache
- ..................
- The first step is to add in the following .htaccess file to tell Apache
- hat we want any and all requests to http://example.com/~joeblogs/ to be
- served up by our circuits.web application.
- Created the .htaccess file in your **Docroot**::
-
- ReWriteEngine On
- ReWriteCond %{REQUEST_FILENAME} !-f
- ReWriteCond %{REQUEST_FILENAME} !-d
- RewriteRule ^(.*)$ /~joeblogs/index.wsgi/$1 [QSA,PT,L]
- Running your Application with Apache/mod_wsgi
- .............................................
- The get your Web Application working and deployed on Apache using mod_wsgi,
- you need to make a few changes to your code. Based on our Basic Hello World
- example earlier, we modify it to the following:
- .. code-block:: python
- :linenos:
-
- #!/usr/bin/env python
-
- from circuits.web import Controller
- from circuits.web.wsgi import Application
-
-
- class Root(Controller):
-
- def index(self):
- return "Hello World!"
-
-
- application = Application() + Root()
- That's it! To run this, save it as index.wsgi and place it in your Web Root
- (public-html or www directory) as per the above guidelines and point your
- favorite Web Browser to: http://example.com/~joeblogs/
- .. note:: It is recommended that you actually use a reverse proxy
- setup for deploying circuits.web web application so that
- you don't loose the advantages and functionality of using
- an event-driven component architecture in your web apps.
-
- In **production** you should use a load balance and reverse
- proxy combination for best performance.