/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

  1. .. module:: circuits
  2. How To Guides
  3. =============
  4. These "How To" guides will steer you in the right direction for common
  5. aspects of modern web applications and website design.
  6. How Do I: Use a Templating Engine
  7. ---------------------------------
  8. circuits.web tries to stay out of your way as much as possible and doesn't
  9. impose any restrictions on what external libraries and tools you can use
  10. throughout your web application or website. As such you can use any template
  11. language/engine you wish.
  12. Example: Using Mako
  13. ...................
  14. This basic example of using the Mako Templating Language.
  15. First a TemplateLookup instance is created. Finally a function called
  16. ``render(name, **d)`` is created that is used by Request Handlers to
  17. render a given template and apply data to it.
  18. Here is the basic example:
  19. .. code-block:: python
  20. :linenos:
  21. #!/usr/bin/env python
  22. import os
  23. import mako
  24. from mako.lookup import TemplateLookup
  25. from circuits.web import Server, Controller
  26. templates = TemplateLookup(
  27. directories=[os.path.join(os.path.dirname(__file__), "tpl")],
  28. module_directory="/tmp",
  29. output_encoding="utf-8"
  30. )
  31. def render(name, **d): #**
  32. try:
  33. return templates.get_template(name).render(**d) #**
  34. except:
  35. return mako.exceptions.html_error_template().render()
  36. class Root(Controller):
  37. def index(self):
  38. return render("index.html")
  39. def submit(self, firstName, lastName):
  40. msg = "Thank you %s %s" % (firstName, lastName)
  41. return render("index.html", message=msg)
  42. (Server(8000) + Root()).run()
  43. Other Examples
  44. ..............
  45. Other Templating engines will be quite similar to integrate.
  46. How Do I: Integrate with a Database
  47. -----------------------------------
  48. .. warning:: Using databases in an asynchronous framework is problematic
  49. because most database implementations don't support asynchronous
  50. I/O operations.
  51. Generally the solution is to use threading to hand off
  52. database operations to a separate thread.
  53. Here are some ways to help integrate databases into your application:
  54. 1. Ensure your queries are optimized and do not block
  55. for extensive periods of time.
  56. 2. Use a library like `SQLAlchemy <http://www.sqlalchemy.org/>`_
  57. that supports multi-threaded database operations
  58. to help prevent your circuits.web web application
  59. from blocking.
  60. 3. *Optionally* take advantage of the :class:`~circuits.Worker`
  61. component to fire :class:`~circuits.task` events wrapping
  62. database calls in a thread or process pool. You can then use
  63. the :meth:`~circuits.Component.call` and :meth:`~.circuits.Component.wait`
  64. synchronization primitives to help with the control flow of
  65. your requests and responses.
  66. Another way you can help improve performance
  67. is by load balancing across multiple backends
  68. of your web application. Using things like
  69. `haproxy <http://haproxy.1wt.eu/>`_ or
  70. `nginx <http://nginx.org/en/>`_ for load balancing
  71. can really help.
  72. How Do I: Use WebSockets
  73. ------------------------
  74. Since the :class:`~circuits.web.websockets.WebSocketDispatcher`
  75. id a circuits.web "dispatcher" it's quite easy to
  76. integrate into your web application. Here's a simple
  77. trivial example:
  78. .. code-block:: python
  79. :linenos:
  80. #!/usr/bin/env python
  81. from circuits.net.events import write
  82. from circuits import Component, Debugger
  83. from circuits.web.dispatchers import WebSockets
  84. from circuits.web import Controller, Logger, Server, Static
  85. class Echo(Component):
  86. channel = "wsserver"
  87. def read(self, sock, data):
  88. self.fireEvent(write(sock, "Received: " + data))
  89. class Root(Controller):
  90. def index(self):
  91. return "Hello World!"
  92. app = Server(("0.0.0.0", 8000))
  93. Debugger().register(app)
  94. Static().register(app)
  95. Echo().register(app)
  96. Root().register(app)
  97. Logger().register(app)
  98. WebSockets("/websocket").register(app)
  99. app.run()
  100. See the `circuits.web examples <https://bitbucket.org/circuits/circuits/src/tip/examples/web>`_.
  101. How do I: Build a Simple Form
  102. -----------------------------
  103. circuits.web parses all POST data as a request comes through and creates a
  104. dictionary of kwargs (Keyword Arguments) that are passed to Request Handlers.
  105. Here is a simple example of handling form data:
  106. .. code-block:: python
  107. :linenos:
  108. #!/usr/bin/env python
  109. from circuits.web import Server, Controller
  110. class Root(Controller):
  111. html = """\
  112. <html>
  113. <head>
  114. <title>Basic Form Handling</title>
  115. </head>
  116. <body>
  117. <h1>Basic Form Handling</h1>
  118. <p>
  119. Example of using
  120. <a href="http://code.google.com/p/circuits/">circuits</a> and it's
  121. <b>Web Components</b> to build a simple web application that handles
  122. some basic form data.
  123. </p>
  124. <form action="submit" method="POST">
  125. <table border="0" rules="none">
  126. <tr>
  127. <td>First Name:</td>
  128. <td><input type="text" name="firstName"></td>
  129. </tr>
  130. <tr>
  131. <td>Last Name:</td>
  132. <td><input type="text" name="lastName"></td>
  133. </tr>
  134. <tr>
  135. <td colspan=2" align="center">
  136. <input type="submit" value="Submit">
  137. </td>
  138. </tr>
  139. </table>
  140. </form>
  141. </body>
  142. </html>"""
  143. def index(self):
  144. return self.html
  145. def submit(self, firstName, lastName):
  146. return "Hello %s %s" % (firstName, lastName)
  147. (Server(8000) + Root()).run(
  148. How Do I: Upload a File
  149. -----------------------
  150. You can easily handle File Uploads as well using the same techniques as above.
  151. Basically the "name" you give your <input> tag of type="file" will get passed
  152. as the Keyword Argument to your Request Handler. It has the following two
  153. attributes::
  154. .filename - The name of the uploaded file.
  155. .value - The contents of the uploaded file.
  156. Here's the code!
  157. .. code-block:: python
  158. :linenos:
  159. #!/usr/bin/env python
  160. from circuits.web import Server, Controller
  161. UPLOAD_FORM = """
  162. <html>
  163. <head>
  164. <title>Upload Form</title>
  165. </head>
  166. <body>
  167. <h1>Upload Form</h1>
  168. <form method="POST" action="/" enctype="multipart/form-data">
  169. Description: <input type="text" name="desc"><br>
  170. <input type="file" name="file">
  171. <input type="submit" value="Submit">
  172. </form>
  173. </body>
  174. </html>
  175. """
  176. UPLOADED_FILE = """
  177. <html>
  178. <head>
  179. <title>Uploaded File</title>
  180. </head>
  181. <body>
  182. <h1>Uploaded File</h1>
  183. <p>
  184. Filename: %s<br>
  185. Description: %s
  186. </p>
  187. <p><b>File Contents:</b></p>
  188. <pre>
  189. %s
  190. </pre>
  191. </body>
  192. </html>
  193. """
  194. class Root(Controller):
  195. def index(self, file=None, desc=""):
  196. if file is None:
  197. return UPLOAD_FORM
  198. else:
  199. filename = file.filename
  200. return UPLOADED_FILE % (file.filename, desc, file.value)
  201. (Server(8000) + Root()).run()
  202. circuits.web automatically handles form and file uploads and gives you access
  203. to the uploaded file via arguments to the request handler after they've been
  204. processed by the dispatcher.
  205. How Do I: Integrate with WSGI Applications
  206. ------------------------------------------
  207. Integrating with other WSGI Applications is
  208. quite easy to do. Simply add in an instance
  209. of the :class:`~circuits.web.wsgi.Gateway`
  210. component into your circuits.web application.
  211. Example:
  212. .. code-block:: python
  213. :linenos:
  214. #!/usr/bin/env python
  215. from circuits.web.wsgi import Gateway
  216. from circuits.web import Controller, Server
  217. def foo(environ, start_response):
  218. start_response("200 OK", [("Content-Type", "text/plain")])
  219. return ["Foo!"]
  220. class Root(Controller):
  221. """App Rot"""
  222. def index(self):
  223. return "Hello World!"
  224. app = Server(("0.0.0.0", 10000))
  225. Root().register(app)
  226. Gateway({"/foo": foo}).register(app)
  227. app.run()
  228. The ``apps`` argument of the :class:`~circuits.web.wsgi.Gateway`
  229. component takes a key/value pair of ``path -> callable``
  230. (*a Python dictionary*) that maps each URI to a given
  231. WSGI callable.
  232. How Do I: Deploy with Apache and mod_wsgi
  233. -----------------------------------------
  234. Here's how to deploy your new Circuits powered Web Application on Apache
  235. using mod_wsgi.
  236. Let's say you have a Web Hosting account with some provider.
  237. - Your Username is: "joblogs"
  238. - Your URL is: http://example.com/~joeblogs/
  239. - Your Docroot is: /home/joeblogs/www/
  240. Configuring Apache
  241. ..................
  242. The first step is to add in the following .htaccess file to tell Apache
  243. hat we want any and all requests to http://example.com/~joeblogs/ to be
  244. served up by our circuits.web application.
  245. Created the .htaccess file in your **Docroot**::
  246. ReWriteEngine On
  247. ReWriteCond %{REQUEST_FILENAME} !-f
  248. ReWriteCond %{REQUEST_FILENAME} !-d
  249. RewriteRule ^(.*)$ /~joeblogs/index.wsgi/$1 [QSA,PT,L]
  250. Running your Application with Apache/mod_wsgi
  251. .............................................
  252. The get your Web Application working and deployed on Apache using mod_wsgi,
  253. you need to make a few changes to your code. Based on our Basic Hello World
  254. example earlier, we modify it to the following:
  255. .. code-block:: python
  256. :linenos:
  257. #!/usr/bin/env python
  258. from circuits.web import Controller
  259. from circuits.web.wsgi import Application
  260. class Root(Controller):
  261. def index(self):
  262. return "Hello World!"
  263. application = Application() + Root()
  264. That's it! To run this, save it as index.wsgi and place it in your Web Root
  265. (public-html or www directory) as per the above guidelines and point your
  266. favorite Web Browser to: http://example.com/~joeblogs/
  267. .. note:: It is recommended that you actually use a reverse proxy
  268. setup for deploying circuits.web web application so that
  269. you don't loose the advantages and functionality of using
  270. an event-driven component architecture in your web apps.
  271. In **production** you should use a load balance and reverse
  272. proxy combination for best performance.