Monday, December 31, 2012

add Python to windows 8

D:\Program Files (x86)\python273;D:\Program Files (x86)\python273\Lib\lib-tk;D:\Program Files (x86)\python273\DLLs;D:\Program Files (x86)\python273\Lib;


google chart tools


in the Google Chart Tools documentation: 
Options: hAxis.showTextEvery

in the code
 new google.visualization.LineChart(document.getElementById('block_line_visualization')).
                    draw(data, { title: "y-axis: Quantity | x-axis: Time-Frame",
                        width: 700, height: 700,
                        vAxis: {maxValue: 6},
                        hAxis: {showTextEvery:1}
        }
            );

Sunday, December 30, 2012

Windows 8 Use touchpad while “typing

http://superuser.com/questions/504571/use-touchpad-while-typing

synpnatics:
in the mouse setting, set the palm check to minimum

Saturday, December 29, 2012

Aggregate Function in Djano Model


>>> from terminal_app.models import CntrOffload
>>> from django.db.models import Min
>>> CntrOffload.objects.aggregate(Min('exec_datetime'))
{'exec_datetime__min': datetime.datetime(2012, 12, 22, 0, 16, 28)}
>>> CntrOffload.objects.filter(vv_c='4588').aggregate(Min('exec_datetime'))
{'exec_datetime__min': datetime.datetime(2012, 12, 26, 3, 30, 32)}
>>>

Bootstrap grid with {%ifchanged xx%}


 <div>
    <div class="row-fluid"></div> #otherwise not in position
    {% for block in blocks %}
        {% if forloop.counter|divisibleby:"6" %}
            <div class="row">
        {% endif %}
        {% ifchanged block.code %}
            <div class="span2">
            {{ block.code }}
            </div>
        {% endifchanged %}
        {% if forloop.counter|divisibleby:"6" %}
            </div>
        {% endif %}
    {% endfor %}
    </div>

Thursday, December 27, 2012

datepicker Enable Specific Dates

http://codeasp.net/blogs/raghav_khunger/microsoft-net/1087/jquery-datepicker-enable-specific-dates
dict['dateObjs'] = AvailableDate.objects.all()

<script type="text/javascript">
        $(function () {

            //'12-22-2012'
            var daysToEnable = [
                {% for dateObj in dateObjs %}
                    '{{ dateObj.date|date:"m-d-Y"}}'{% if not forloop.last %},{% endif %}
                {% endfor %}
            ];

            $('#datepicker').datepicker({
                beforeShowDay: enableSpecificDates,
                dateFormat: 'M. dd, yy'
            });

            function enableSpecificDates(date) {
                var month = date.getMonth();
                var day = date.getDate();
                var year = date.getFullYear();
                for (i = 0; i < daysToEnable.length; i++) {
                    if ($.inArray((month + 1) + '-' + day + '-' + year, daysToEnable) != -1) {
                        return [true];
                    }
                }
                return [false];
            }
        });

Popover


<a href="#" class="btn btn-primary btn-small changeButton"
        rel="popover" data-placement="bottom"
        data-html="true"
        data-content="
        <form action='/start/' method='post'>{% csrf_token %}
    <fieldset>
        <legend>Date Selection</legend>
        <label>date:
            <input type='text' id='datepicker' name='date' placeholder='date of view'>
        </label>
        <button type='submit' class='btn btn-primary btn-small' value='submit'>Submit</button>
    </fieldset>
    </form>"
        >
    Change</a>


ajax

loading page
loading animation
http://jsfiddle.net/VpDUG/1844/

Wednesday, December 26, 2012

Bootstrap Carousel


<div id="myCarousel" class="carousel slide">
        <!-- Carousel items -->
        <div class="carousel-inner">
            <div class="active item">
                <img src="http://www.act.com.hk/images/en_leading06_psa.jpg" alt="">
                <div class="carousel-caption">
                    <h4>First Thumbnail label</h4>
                    <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
                </div>
            </div>
            <div class="item">

            </div>
            <div class="item">

            </div>
        </div>
        <!-- Carousel nav -->
        <a class="carousel-control left" href="#myCarousel" data-slide="prev">&lsaquo;</a>
        <a class="carousel-control right" href="#myCarousel" data-slide="next">&rsaquo;</a>
    </div>

Tuesday, December 25, 2012

Python dictionary and zip


  1. len(myDict)
  2. eletment in myDict
  3. for key in myDict:
  4. for key in myDict.keys()
  5. for key, value in myDict.items():
  6. for value in myDict.values():

  1. myDict,ckear()
  2. aDict,update(bDict) - for each key in bDict, updates aDict with that key/value pair

zip() is the built-in function
  1. zip("abc", [1, 2, 3])  --> [(‘a’,1),(‘b’,2),(‘c’,3)] 
  2. dict(zip("abc", [1, 2, 3]) -->{'a': 1, 'c': 3, 'b': 2} 

aString = "\n".join(aList)
aList = aString.split("\n")

OrderedDict
reports = collections.OrderedDict()
the OrderedDict will remember the sequence of key insertion

Chrome Shortcuts on Windows


source: https://support.google.com/chrome/bin/answer.py?hl=en&answer=157179


Tab and window shortcuts

Ctrl+NOpens a new window.
Ctrl+TOpens a new tab.
Ctrl+Shift+NOpens a new window in incognito mode.
Press Ctrl+O, then select file.Opens a file from your computer in Google Chrome.
Press Ctrl and click a link. Or click a link with your middle mouse button (or mousewheel).Opens the link in a new tab in the background .
Press Ctrl+Shift and click a link. Or pressShift and click a link with your middle mouse button (or mousewheel).Opens the link in a new tab and switches to the newly opened tab.
Press Shift and click a link.Opens the link in a new window.
Ctrl+Shift+TReopens the last tab you've closed. Google Chrome remembers the last 10 tabs you've closed.
Drag a link to a tab.Opens the link in the tab.
Drag a link to a blank area on the tab strip.Opens the link in a new tab.
Drag a tab out of the tab strip.Opens the tab in a new window.
Drag a tab out of the tab strip and into an existing window.Opens the tab in the existing window.
Press Esc while dragging a tab.Returns the tab to its original position.
Ctrl+1 through Ctrl+8Switches to the tab at the specified position number on the tab strip.
Ctrl+9Switches to the last tab.
Ctrl+Tab or Ctrl+PgDownSwitches to the next tab.
Ctrl+Shift+Tab or Ctrl+PgUpSwitches to the previous tab.
Alt+F4 or Ctrl + Shift + WCloses the current window.
Ctrl+W or Ctrl+F4Closes the current tab or pop-up.
Click a tab with your middle mouse button (or mousewheel).Closes the tab you clicked.
Right-click, or click and hold either the Back or Forward arrow in the browser toolbar.Displays your browsing history in the tab.
Press Backspace, or Alt and the left arrow together.Goes to the previous page in your browsing history for the tab.
Press Shift+Backspace, or Alt and the right arrow together.Goes to the next page in your browsing history for the tab.
Press Ctrl and click either the Back arrow, Forward arrow, or Go button in the toolbar. Or click either button with your middle mouse button (or mousewheel).Opens the button destination in a new tab in the background.
Double-click the blank area on the tab strip.Maximizes or minimizes the window.
Alt+HomeOpens your homepage in your current window.

Google Chrome feature shortcuts

Alt+F or Alt+E or F10Opens the Chrome menu Chrome menu, which lets you customize and control settings in Google Chrome.
Ctrl+Shift+BToggles the bookmarks bar on and off.
Ctrl+HOpens the History page.
Ctrl+JOpens the Downloads page.
Shift+EscOpens the Task Manager.
Shift+Alt+TSets focus on the first tool in the browser toolbar. You can then use the following shortcuts to move around in the toolbar:
  • Press Tab, Shift+Tab, Home, End, right arrow, and left arrow to move focus to different items in the toolbar.
  • Press Space or Enter to activate toolbar buttons, including page actions and browser actions.
  • Press Shift+F10 to bring up any associated context menu (e.g. browsing history for the Back button).
  • Press Esc to return focus from the toolbar back to the page.
F6 or Shift+F6Switches focus to the next keyboard-accessible pane. Panes include:
  • Highlights the URL in the address bar
  • Bookmarks bar (if visible)
  • The main web content (including any infobars)
  • Downloads bar (if visible)
Ctrl+Shift+JOpens Developer Tools.
Ctrl+Shift+DeleteOpens the Clear Browsing Data dialog.
F1Opens the Help Center in a new tab (our favorite).
Ctrl+Shift+MSwitch between multiple users.

Address bar shortcuts

Use the following shortcuts in the address bar:
Type a search term, then press Enter.Performs a search using your default search engine.
Type a search engine keyword, press Space, type a search term, and press Enter.Performs a search using the search engine associated with the keyword.
Begin typing a search engine URL, press Tabwhen prompted, type a search term, and press Enter.Performs a search using the search engine associated with the URL.
Ctrl+EnterAdds www. and .com to your input in the address bar and open the resulting URL.
Type a URL, then press Alt+Enter.Opens the URL in a new tab.
Ctrl+L or Alt+DHighlights the URL.
Ctrl+K or Ctrl+EPlaces a '?' in the address bar. Type a search term after the question mark to perform a search using your default search engine.
Press Ctrl and the left arrow together.Moves your cursor to the preceding key term in the address bar
Press Ctrl and the right arrow together.Moves your cursor to the next key term in the address bar
Ctrl+BackspaceDeletes the key term that precedes your cursor in the address bar
Select an entry in the address bar drop-down menu with your keyboard arrows, then pressShift+Delete.Deletes the entry from your browsing history, if possible.
Click an entry in the address bar drop-down menu with your middle mouse button (or mousewheel).Opens the entry in a new tab in the background.
Press Page Up or Page Down when the address bar drop-down menu is visible.Selects the first or last entry in the drop-down menu.

Webpage shortcuts

Ctrl+PPrints your current page.
Ctrl+SSaves your current page.
F5 or Ctrl+RReloads your current page.
EscStops the loading of your current page.
Ctrl+FOpens the find bar.
Ctrl+G or F3Finds the next match for your input in the find bar.
Ctrl+Shift+GShift+F3, or Shift+EnterFinds the previous match for your input in the find bar.
Click the middle mouse button (or mousewheel).Activates auto-scrolling. As you move your mouse, the page automatically scrolls according to the direction of the mouse.
Ctrl+F5 or Shift+F5Reloads your current page, ignoring cached content.
Press Alt and click a link.Downloads the target of the link.
Ctrl+UOpens the source of your current page.
Drag a link to bookmarks barSaves the link as a bookmark.
Ctrl+DSaves your current webpage as a bookmark.
Ctrl+Shift+DSaves all open pages as bookmarks in a new folder.
F11Opens your page in full-screen mode. PressF11 again to exit full-screen.
Ctrl and +, or press Ctrl and scroll your mousewheel up.Enlarges everything on the page.
Ctrl and -, or press Ctrl and scroll your mousewheel down.Makes everything on the page smaller.
Ctrl+0Returns everything on the page to normal size.
Space barScrolls down the web page.
HomeGoes to the top of the page.
EndGoes to the bottom of the page.
Press Shift and scroll your mousewheel.Scrolls horizontally on the page.

Text shortcuts

Ctrl+CCopies highlighted content to the clipboard.
Ctrl+V or Shift+InsertPastes content from the clipboard.
Ctrl+Shift+VPaste content from the clipboard without formatting.
Ctrl+X or Shift+DeleteDeletes the highlighted content and copies it to the clipboard.

student bid

studentlink -> others -> student sale

Monday, December 24, 2012

access key and element in dictionary in Django Template


{% for key, value in dictionary.items %}
    <p>{{ key }} - {{ value }}</p>
{% endfor %}

Subversion

Subversion version control

  1. Assembla
  2. My Start
  3. Free Private SVN Subversion (scroll down, there is a free onw)
  4. Import the root directory of your folder (PyCharm VCS)
  5. Username and password (username is Assembla Log in ID rather than Email Address)
  6. VCS -> Enable Subversion Intergarion

Sunday, December 23, 2012

Chapter 12: Deploying Django

Deploying Django Application to a Production Server
Apache: industrial-strength Web Server


Preparing Your Codebase for Production


1. settings.py
DEBUG = False
TEMPLATE_DEBUG = False

2. root template directory
404.html

{% extends "base.html" %}

{% block title %}Page not found{% endblock %}

{% block content %}
<h1>Page not found</h1>

<p>Sorry, but the requested page could not be found.</p>
{% endblock %}

500.html
 (not rely on other templates)
unhandled Python exception

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
    <title>Page unavailable</title>
</head>
<body>
    <h1>Page unavailable</h1>

    <p>Sorry, but the requested page is unavailable due to a
    server hiccup.</p>

    <p>Our engineers have been notified, so check back later.</p>
</body>
</html>3. Setting Up Error Alters and Setting Up Broken Link Alerts
Need Email Set up

ADMINS = (
    ('John Lennon', 'jlennon@example.com'),
    ('Paul McCartney', 'pmacca@example.com'),
)

http://www.djangobook.com/en/2.0/chapter12.html


Using Multiple settings.py for Development and Production


Three ways:

  1. Two independent settings files
  2. base setting file and inherited setting file
  3. one single setting.py with Python logic to change the settings based on context
1. Copy as settings.py settings_production.py
2. IMPORT
# settings.py

DEBUG = True
TEMPLATE_DEBUG = DEBUG

DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME = 'devdb'
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_PORT = ''

# ...

# settings_production.py

from settings import *

DEBUG = TEMPLATE_DEBUG = False
DATABASE_NAME = 'production'
DATABASE_USER = 'app'
DATABASE_PASSWORD = 'letmein'

override
3. use of Python logic

# settings.py

import socket

if socket.gethostname() == 'my-laptop':
    DEBUG = TEMPLATE_DEBUG = True
else:
    DEBUG = TEMPLATE_DEBUG = False

# ...

IF the name of settings changed:
 You can fix this  by editing manage.py to change settingsto the name of your module

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "quality.settings_XXX")





Apache + mod_python

Basic Configuration


make sure you have Apache installed with the mod_python module activated.
LoadModule python_module /usr/lib/apache2/modules/mod_python.so
Then, edit your Apache configuration file and add a <Location> directive that ties a specific URL path to a specific Django installation.
<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonDebug Off
</Location>


Make sure to replace mysite.settings with the appropriate DJANGO_SETTINGS_MODULE for your site.

This tells Apache, “Use mod_python for any URL at or under ‘/’, using the Django mod_python handler.” It passes the value of DJANGO_SETTINGS_MODULE so mod_python knows which settings to use.

Note that we’re using the <Location> directive, not the <Directory> directive. The latter is used for pointing at places on your filesystem, whereas <Location> points at places in the URL structure of a Web site. <Directory> would be meaningless here.

Apache likely runs as a different user than your normal login and may have a different path and sys.path. You may need to tell mod_python how to find your project and Django itself.
PythonPath "['/path/to/project', '/path/to/django'] + sys.path"

Running Multiple Django Installations on the Same Apache Instance

to be continued

Running a Development Server with mod_python

Serving Django and Media Files from the Same Apache Instance

Alternative: 

Using Django with FastCGI

http://www.djangobook.com/en/2.0/chapter12.html



Scaling

_images/scaling-1.png
Figure 12-1: a single server Django setup.
However, as traffic increases you’ll quickly run into resource contention between the different pieces of software.
_images/scaling-2.png
Figure 12-2: Moving the database onto a dedicated server
As far as Django is concerned, the process of separating out the database server is extremely easy: you’ll simply need to change the DATABASE_HOST setting to the IP or DNS name of your database server. It’s probably a good idea to use the IP if at all possible, as relying on DNS for the connection between your Web server and database server isn’t recommended.
_images/scaling-3.png
Figure 12-3: Separating out the media server.
For sites heavy in static content (photos, videos, etc.), moving to a separate media server is doubly important. Three-server setup: 10 million hits a day
_images/scaling-4.png
Figure 12-4: A load-balanced, redundant server setup.
_images/scaling-5.png
Figure 12-5. An example large-scale Django setup.



Performance Tuning

There’s No Such Thing As Too Much RAM

This shouldn’t be too hard; we’ve developed a site with more than half a million newspaper articles, and it took under 2GB of space.

Turn Off Keep-Alive

Use memcached

Of course, selecting memcached does you no good if you don’t actually use it. Chapter 15 is your best friend here: learn how to use Django’s cache framework, and use it everywhere possible. Aggressive, preemptive caching is usually the only thing that will keep a site up under major traffic.
done


done

Chapter 9 Advanced Template

context is a name -> value mapping (similar to a Python dictionary) that is passed to a template.

RequestContext and Context Processors



instance of django.template.Context, but Django also comes with a special subclass, django.template.RequestContext, that acts slightly differently. RequestContextadds a bunch of variables to your template context by default – things like the HttpRequest object or information about the currently logged-in user.

Use RequestContext when you don’t want to have to specify the same set of variables in a series of templates.
Avoid repeating the same values in the context dictionary 

Point: a context with many similar elements
DO NOT REPEAT YOURSELF

MOST LOW-LEVEL
 c1 = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am view 1.'
    })
 c1 = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the second view.'
    })
from django.template import loader, RequestContext

def custom_proc(request):
    "A context processor that provides 'app', 'user' and 'ip_address'."
    return {
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR']
    }

def view_1(request):
    # ...
    t = loader.get_template('template1.html')
    c = RequestContext(request, {'message': 'I am view 1.'},
            processors=[custom_proc])
    return t.render(c)

def view_2(request):
    # ...
    t = loader.get_template('template2.html')
    c = RequestContext(request, {'message': 'I am the second view.'},
            processors=[custom_proc]) #list
    return t.render(c)
context processor: it takes an HttpRequest object and returns a dictionary of variables to use in the template context
# naming convention: processorName_proc

processors argument, which is a list or tuple of context processor functions to use. 

render_to_reponse() + RequestContext

def view_2(request):
    # ...
    return render_to_response('template2.html',
        {'message': 'I am the second view.'},
        context_instance=RequestContext(request, processors=[custom_proc]))
END MOST LOW-LEVEL

GLOBAL CONTEXT-LEVEL (Not really understood)
global context processors
This removes the need to specify processors each time you use RequestContext

For that reason, Django provides support for global context processors. The TEMPLATE_CONTEXT_PROCESSORSsetting (in your settings.py) designates which context processors should always be applied to RequestContext.

This setting is a tuple of callables that use the same interface as our custom_proc function above – functions that take a request object as their argument and return a dictionary of items to be merged into the context.
TEMPLATE_CONTEXT_PROCESSORS = ( #tuple
    'django.core.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
)

django.core.context_processors.auth

  • user
  • messages
  • perms

django.core.context_processors.debug

  • debug
  • sql_queries

django.core.context_processors.i18n

  • LANGUAGES
  • LANGUAGE_CODE

Auto escape
(template variable, variable from view functions)
{{ data|default:"3 &lt; 2" }}
...rather than
{{ data|default:"3 < 2" }}  <-- Bad! Don't do this.
This doesn’t affect what happens to data coming from the variable itself. 
Generally, template authors don’t need to worry about auto-escaping very much. Developers on the Python side (people writing views and custom filters) need to think about the cases in which data shouldn’t be escaped, and mark data appropriately, so things work in the template.

Inside Template Loading

Generally, you’ll store templates in files on your filesystem, but you can use custom template loaders to load templates from other sources.

Django has two ways to load templates:
  1. django.template.loader.get_template(template_name)
  2. django.template.loader.select_template(template_name_list)
these two functions by default uses your TEMPLATE_DIRS setting to load templates. Internally, however, these functions actually delegate to a template loader for the heavy lifting.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
# 'django.template.loaders.eggs.Loader',
)
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'template').replace('\\','/'),
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)




django.template.loaders.filesystem.load_template_source: This loader loads templates from the filesystem, according to TEMPLATE_DIRS. It is enabled by default.


django.template.loaders.app_directories.load_template_source: This loader loads templates from Django applications on the filesystem. For each application in INSTALLED_APPS, the loader looks for a templates subdirectory. If the directory exists, Django looks for templates there.

This means you can store templates with your individual applications, making it easy to distribute Django applications with default templates. For example, if INSTALLED_APPS contains('myproject.polls', 'myproject.music'), then get_template('foo.html') will look for templates in this order:
/path/to/myproject/polls/templates/foo.html
/path/to/myproject/music/templates/foo.html


Note that the loader performs an optimization when it is first imported: it caches a list of which INSTALLED_APPS packages have a templates subdirectory.

This loader is enabled by default.

django.template.loaders.eggs.load_template_source: This loader is just like app_directories, except it loads templates from Python eggs rather than from the filesystem. This loader is disabled by default; you’ll need to enable it if you’re using eggs to distribute your application. (Python eggs are a way of compressing Python code into a single file.)

Django uses the template loaders in order according to the TEMPLATE_LOADERS setting. It uses each loader until a loader finds a match.

Extending the Template System

 custom template tags and/or filters

Creating a Template Library

to create a template library – a small bit of infrastructure Django can hook into.

Three steps of creating template library
  1. First, create another app solely for the template library.  because your filters might be useful to you in future projects, add the app to your INSTALLED_APPS setting
  2. Second, create a templatetags directory in the appropriate Django application’s package. It should be on the same level as models.py, views.py, and so forth. For example:
  3. __init__.py and then your custom tag/filter defintions

books/
    __init__.py
    models.py
    templatetags/ __init__.py; poll_extras.py etc.
    views.py

correspond to the poll_extras.py # your custom defined tag/filter
{% load poll_extras %} //later loading in the template

The {% load %} tag looks at your INSTALLED_APPS setting (new app for template library must be added in setting)and only allows the loading of template libraries within installed Django applications. This is a security feature; it allows you to host Python code for many template libraries on a single computer without enabling access to all of them for every Django installation.

first thing in poll_extras.py is registration: 
from django import template

register = template.Library()
Later:
register.filter('cut', cut)
register.filter('lower', lower)

Writing Custom Template Filters

  • __init__.py inside app folder, and inside templatetags folder (under yourApp folder)
  • app name in the INSTALLED_APPS section from settings.py
  • to use: {% load customizedfilter %}s

Custom filters are just Python functions that take one or two arguments:
  • The value of the variable (input)
  • The value of the argument, which can have a default value or be left out altogether
For example, in the filter {{ var|foo:"bar" }}, the filter foo would be passed the contents of the variable var and the argument "bar".

Rule for filter functions:
. They shouldn’t raise exceptions, and they should fail silently. If there’s an error, they should return either the original input or an empty string

def cut(value, arg):
    "Removes all values of arg from the given string"
    return value.replace(arg, '')
{{ somevariable|cut:" " }} 

def lower(value): # Only one argument.
    "Converts a string into all lowercase"
    return value.lower()
register.filter('cut', cut)
register.filter('lower', lower)
using decorator
from django import template

register = template.Library()

@register.filter #OR @register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')
done

Writing Custom Template Tags 


When Django compiles a template, it splits the raw template text into nodes. Each node is an instance of django.template.Node and has a render() method. Thus, a compiled template is simply a list of Node objects. For example, consider this template:

Hello, {{ person.name }}.

{% ifequal name.birthday today %}
    Happy birthday!
{% else %}
    Be sure to come back on your birthday
    for a splendid surprise message.
{% endifequal %}

In compiled template form, this template is represented as this list of nodes:
  • Text node: "Hello, "
  • Variable node: person.name
  • Text node: ".\n\n"
  • IfEqual node: name.birthday and today
When you call render() on a compiled template, the template calls render() on each Node in its node list
Thus, to define a custom template tag, you specify how the raw template tag is converted into a Node (the compilation function) and what the node’s render() method does.


1 Writing the Compilation Function

For each template tag the parser encounters, it calls a Python function with the tag contents and the parser object itself. This function is responsible for returning a Node instance based on the contents of the tag.
{% xxx %}  parser
User-defined {% current_time %} #actually available: {% now %}

<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>

token.contents is a string of the raw contents of the tag. In our example, it’s'current_time "%Y-%m-%d %I:%M %p"'.
from django import template

register = template.Library()

def do_current_time(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, format_string = token.split_contents()
    except ValueError:
        msg = '%r tag requires a single argument' % token.split_contents()[0]
        raise template.TemplateSyntaxError(msg)
    return CurrentTimeNode(format_string[1:-1])

parser is the template parser object. We don’t use it in this example. token is the token currently being parsed by the parser.
  1. Don’t hard-code the tag’s name in your error messages, because that couples the tag’s name to your function. token.split_contents()[0] will always be the name of your tag – even when the tag has no arguments.
  2. The function returns a CurrentTimeNode (which we’ll create shortly) containing everything the node needs to know about this tag. In this case, it just passes the argument "%Y-%m-%d %I:%M %p". The leading and trailing quotes from the template tag are removed with format_string[1:-1].
  3. Template tag compilation functions must return a Node subclass; any other return value is an error.

2 Writing the Template Node

import datetime

class CurrentTimeNode(template.Node): #extends
    def __init__(self, format_string):
        self.format_string = str(format_string)

    def render(self, context):
        now = datetime.datetime.now()
        return now.strftime(self.format_string)

3 Registering the Tag

register.tag('current_time', do_current_time)
As with filter registration, it is also possible to use register.tag as a decorator in Python 2.4 and above:
@register.tag(name="current_time")
def do_current_time(parser, token):
    # ...

@register.tag
def shout(parser, token):
    # ...

------------------------------------------------------------------------------------------------

Setting a Variable in the Context

The previous section’s example simply returned a value. Often it’s useful to set template variables instead of returning values.

return a value vs. set a value
dictionary assignment
class CurrentTimeNode2(template.Node):
    def __init__(self, format_string):
        self.format_string = str(format_string)

    def render(self, context):
        now = datetime.datetime.now()
        context['current_time'] = now.strftime(self.format_string)
        return ''
Note that render() returns an empty string. render() should always return a string, so if all the template tag does is set a variable, render() should return an empty string.

New version: setting template variable through context dictionary
(hard-coded)
{% current_time2 "%Y-%M-%d %I:%M %p" %}
<p>The time is {{ current_time }}.</p>
(non hard-coded)

{% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}
<p>The current time is {{ my_current_time }}.</p>

To do so:
import re

class CurrentTimeNode3(template.Node):
    def __init__(self, format_string, var_name):
        self.format_string = str(format_string)
        self.var_name = var_name

    def render(self, context):
        now = datetime.datetime.now()
        context[self.var_name] = now.strftime(self.format_string)
        return ''

def do_current_time(parser, token):
    # This version uses a regular expression to parse tag contents.
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        msg = '%r tag requires arguments' % token.contents[0]
        raise template.TemplateSyntaxError(msg)

    m = re.search(r'(.*?) as (\w+)', arg)
    if m:
        fmt, var_name = m.groups()
    else:
        msg = '%r tag had invalid arguments' % tag_name
        raise template.TemplateSyntaxError(msg)

    if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")):
        msg = "%r tag's argument should be in quotes" % tag_name
        raise template.TemplateSyntaxError(msg)

    return CurrentTimeNode3(fmt[1:-1], var_name)

Re: Regular expression operations

Parsing Until Another Template Tag

Template tags can work as blocks containing other tags (like {% if %}, {% for %}, etc.). To create a template tag like this, use parser.parse() in your compilation function.

Comment Tag:
def do_comment(parser, token):
    nodelist = parser.parse(('endcomment',)) #parse until; return nodeList
    parser.delete_first_token() # not counting {% comment %} and {% endcomment %}
    return CommentNode()

class CommentNode(template.Node):
    def render(self, context):
        return ''


parser.parse() takes a tuple of names of template tags to parse until. It returns an instance ofdjango.template.NodeList, which is a list of all Node objects that the parser encountered before it encountered any of the tags named in the tuple.

So in the preceding example, nodelist is a list of all nodes between {% comment %} and {% endcomment %}, not counting {% comment %} and {% endcomment %} themselves.

After parser.parse() is called, the parser (not nodeList from parser.parse) hasn’t yet “consumed” the {% endcomment %} tag, so the code needs to explicitly call parser.delete_first_token() to prevent that tag from being processed twice.

Parsing Until Another Template Tag and Saving Contents {% endXXXX %}

{% upper %}
    This will appear in uppercase, {{ user_name }}.
{% endupper %}

def do_upper(parser, token):
    nodelist = parser.parse(('endupper',))
    parser.delete_first_token()
    return UpperNode(nodelist)

class UpperNode(template.Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist

    def render(self, context):
        output = self.nodelist.render(context) #calls render() one each node
        return output.upper()

The only new concept here is self.nodelist.render(context) in UpperNode.render(). This simply calls render() on eachNode in the node list.

Shortcut for Simple Tags

take a single argument, and return a string -> shortcut for it
def current_time(format_string):
    try:
        return datetime.datetime.now().strftime(str(format_string))
    except UnicodeEncodeError:
        return ''

register.simple_tag(current_time)

@register.simple_tag
def current_time(token):
    # ...

  • Single: Only the (single) argument is passed into our function.
  • Error Checking: Checking for the required number of arguments has already been done by the time our function is called, so we don’t need to do that.
  • Strip: The quotes around the argument (if any) have already been stripped away, so we receive a plain Unicode string.

Inclusion Tags

Another common template tag is the type that displays some data by rendering ANOTHER template
effect:
{% books_for_author author %}

result in
<ul>
    <li>The Cat In The Hat</li>
    <li>Hop On Pop</li>
    <li>Green Eggs And Ham</li>
</ul>

1. function

def books_for_author(author):
    books = Book.objects.filter(authors__id=author.id)
    return {'books': books}

2. Template

<ul>
{% for book in books %}
    <li>{{ book.title }}</li>
{% endfor %}
</ul>

3. Register

register.inclusion_tag('book_snippet.html')(books_for_author)
shortcut:
@register.inclusion_tag('book_snippet.html')
def books_for_author(author):
    # ...

>>>

takes_context: Sometimes, your inclusion tags need access to values from the parent template’s context
@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
    return {
        'link': context['home_link'],
        'title': context['home_title'],
    }
link.html template:
Jump directly to <a href="{{ link }}">{{ title }}</a>.
use the inclusion tag:
{% jump_link %}

Writing Custom Template Loaders

special loading logic. For example, you could load templates from a database, or directly from a Subversion repository using Subversion’s Python bindings, or (as shown shortly) from a ZIP archive.
to be continued...
http://www.djangobook.com/en/2.0/chapter09.html





done