PageRenderTime 45ms CodeModel.GetById 33ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

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