Dynamic images in your component views
Dynamic images are often used in web apps, for example to create charts from database data. You create your image in a function and then return the content of the image, with the appropriate response headers.
In many frameworks, you must create a method, bind it to an URL and include this URL in the src property of an img tag. In Nagare you directly associate the method to the img tag.
In this example I've taken the Nagare logo and wrote the current timestamp on it using PIL.
Here's the class definition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import cStringIO import time import pkg_resources from PIL import Image, ImageDraw from nagare import presentation class MyApp(object): def get_image(self, h): """Returns the modified Nagare logo In: - ``h`` -- the renderer (not used) """ # Get the Nagare logo requirement = pkg_resources.Requirement.parse('nagare') img = pkg_resources.resource_stream(requirement, '/static/img/logo.gif') # Open the image im = Image.open(img) draw = ImageDraw.Draw(im) # Draw timestamp on it draw.text((15, 10), time.asctime(), fill=0xffffff) # Save it into a StringIO and return the content imgdata = cStringIO.StringIO() im.save(imgdata, 'PNG') return imgdata.getvalue() |
Nothing special here. The interesting part is the rendering method for this component:
1 2 3 | @presentation.render_for(MyApp) def render(self, h, *args): return h.img.action(self.get_image) |
You may have already used the action() method on HTML tags such as a or input. The img tags also have one.
All you have to do is to give to action() a callback, that will take a renderer as argument and will have to return the image data. So I passed to action() the get_image() method of MyApp class. As the function returns the image stream, without explicitly setting the content-type of the HTTP response, Nagare auto discovers the content type and set it by itself.
Common HTML structuration for different components
The problem
We want to obtain the same HTML structure when rendering different components.
Templating using inheritance and named views
The principle is the same than the template method design pattern but applied to components views.
Setting the base class
Let's declare an empty base class:
class BaseEntry(object):
pass
Its default view sets the common HTML structure and embeds the title and properties named views:
from nagare.presentation import render_for
@render_for(BaseEntry)
def render_BaseEntry(self, h, comp, *args):
"""All the views of entries will have this HTML structure"""
with h.div(class_='entry'):
with h.div(class_='title'):
h << comp.render(h, model='title')
with h.div(class_='properties'):
h << comp.render(h, model='properties')
return h.root
The BaseEntry's title and properties views are empty. They will be redefined for the derived classes:
@render_for(BaseEntry, model='title')
def render_BaseEntry_header(self, h, *args):
return h.root
@render_for(BaseEntry, model='properties')
def render_BaseEntry_header(self, h, *args):
return h.root
We can now verify that the basic HTML structure is generated:
>>> from nagare import component
>>> from nagare.namespaces import xhtml
>>>
>>> entry = component.Component(BaseEntry())
>>> h = xhtml.Renderer()
>>> print entry.render(h).write_htmlstring(pretty_print=True)
<div class="entry">
<div class="title"></div>
<div class="properties"></div>
</div>
Using the base class
Now, let's create a derived class:
class SimpleEntry(BaseEntry):
def __init__(self, name, firstname, age):
self.name = name
self.firstname = firstname
self.age = age
We only have to define its title and properties views:
@render_for(SimpleEntry, model='title')
def render_SimpleEntry_title(self, h, *args):
return '%s %s' % (self.firstname, self.name)
@render_for(SimpleEntry, model='properties')
def render_SimpleEntry_properties(self, h, *args):
return h.div('Age: %d' % self.age)
for the component data to be correctly inserted into the right HTML structure:
>>> from nagare import component
>>> from nagare.namespaces import xhtml
>>>
>>> entry = component.Component(SimpleEntry('John', 'Doe', 20))
>>> h = xhtml.Renderer()
>>> print entry.render(h).write_htmlstring(pretty_print=True)
<div class="entry">
<div class="title">Doe John</div>
<div class="properties"><div>Age: 20</div></div>
</div>
Templating using aggregation and named views
When inheritance is not wanted, we can define a component whose job is to do the rendering:
class EntryTemplate(object):
def __init__(self, entry):
self.entry = entry
@render_for(EntryTemplate)
def EntryTemplate(self, h, *args):
"""
Same code that we used in ``render_BaseEntry``
but using ``self.entry`` instead of ``self``.
"""
with h.div(class_='entry'):
with h.div(class_='title'):
h << self.entry.render(h, model='title')
with h.div(class_='properties'):
h << self.entry.render(h, model='properties')
return h.root
Let's use it with our SimpleEntry class:
class SimpleEntry(object): # no inheritance anymore
def __init__(self, name, firstname, age):
self.name = name
self.firstname = firstname
self.age = age
The title and properties views are still the same, but we must now declare a default view to wrap our SimpleEntry component into a EntryTemplate component and render it:
from nagare.component import Component
@render_for(SimpleEntry)
def render_SimpleEntry_header(self, h, comp, *args):
return Component(EntryTemplate(comp)).render(h)
Then we can use the SimpleEntry components exactly like in the previous chapter:
>>> from nagare import component
>>> from nagare.namespaces import xhtml
>>>
>>> entry = component.Component(SimpleEntry('John', 'Doe', 20))
>>> h = xhtml.Renderer()
>>> print entry.render(h).write_htmlstring(pretty_print=True)
<div class="entry">
<div class="title">Doe John</div>
<div class="properties"><div>Age: 20</div></div>
</div>
rss