Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Introduction

How many CPU cycles do you suppose are wasted on blogs that are generated every request? Wouldn’t it make more sense to generate them only when they’re updated? StaticGenerator is a Python class for Django that makes it easy to create static files for lightning fast performance.
How many CPU cycles do you suppose are wasted on blogs that are generated every request? Wouldn’t it make more sense to generate them only when they’re updated? StaticGenerator is a Python class for Django that makes it easy to create static files for lightning fast performance.

## Fork

Expand All @@ -11,27 +11,29 @@ This is a fork from the main branch in order to add patches from [bolhoed](https
In short, this adds the ability to only cache for anonymous users and to add the ability to exclude urls:

WEB_ROOT = os.path.join(os.path.dirname(__file__), 'generated')

STATIC_GENERATOR_ANONYMOUS_ONLY = True

STATIC_GENERATOR_URLS = (
r'^/$',
r'^/(articles|projects|about)',
)

STATIC_GENERATOR_EXCLUDE_URLS = (
r'\.xml$',
r'^/articles/search',
r'^/articles/feed',
r'^/articles/comments/posted',
)



## Download

You can get StaticGenerator using `easy_install`:

easy_install staticgenerator

Or download from the [cheeseshop](http://pypi.python.org/pypi/staticgenerator/1.3).

## Usage
Expand All @@ -51,7 +53,13 @@ First, add Regexes of URLs you want to cache to `settings.py` like so:
r'^/blog',
r'^/about',
)


STATIC_GENERATOR_QUERYSTRINGS = (
r'^page$',
r'^print$',
r'^debug$',
)

Second, add the Middleware to `MIDDLEWARE_CLASSES`:

MIDDLEWARE_CLASSES = (
Expand All @@ -60,9 +68,9 @@ Second, add the Middleware to `MIDDLEWARE_CLASSES`:
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
...snip...
)

**Note**: You must place the StaticGeneratorMiddleware before FlatpageFallbackMiddleware if you use it.

When the pages are accessed for the first time, the body of the page is saved into a static file. This is completely transparent to the end-user. When the page or an associated object has changed, simply delete the cached file (See notes on Signals).

### Method 2: Generate on Save
Expand Down Expand Up @@ -111,7 +119,7 @@ Every time you save a Post or FlatPage it deletes the static file (notice that I

dispatcher.connect(publish_comment, sender=Comment, signal=signals.post_save)
dispatcher.connect(publish_comment, sender=FreeComment, signal=signals.post_save)

## Configure your front-end

### Sample Nginx configuration
Expand All @@ -120,12 +128,12 @@ This configuration snippet shows how Nginx can automatically show the index.html

# This example configuration only shows parts relevant to a Django app
http {

upstream django {
# Apache/mod_python running on port 7000
server example.com:7000;
}

server {
server_name example.com;
root /var/www/;
Expand All @@ -144,7 +152,7 @@ This configuration snippet shows how Nginx can automatically show the index.html
}
}
}

## It’s not for Everything

The beauty of the generator is that you choose when and what urls are made into static files. Obviously a contact form or search form won’t work this way, so we just leave them as regular Django requests. In your front-end http server (you are using a front-end web server, right?) just set the URLs you want to be served as static and they’re already being served.
Expand Down
8 changes: 4 additions & 4 deletions staticgenerator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class StaticGenerator(object):
from staticgenerator import quick_publish
quick_publish('/', Post.objects.live(), FlatPage)

The class accepts a list of 'resources' which can be any of the
following: URL path (string), Model (class or instance), Manager, or
The class accepts a list of 'resources' which can be any of the
following: URL path (string), Model (class or instance), Manager, or
QuerySet.

As of v1.1, StaticGenerator includes file and path deletion::
Expand Down Expand Up @@ -63,7 +63,7 @@ def parse_dependencies(self, kw):
settings = kw.get('settings', None)
site = kw.get('site', None)
fs = kw.get('fs', None)

self.http_request = http_request
if not http_request:
from django.http import HttpRequest
Expand Down Expand Up @@ -203,7 +203,7 @@ def get_filename_from_path(self, path, query_string):

def publish_from_path(self, path, query_string=None, content=None):
"""
Gets filename and content for a path, attempts to create directory if
Gets filename and content for a path, attempts to create directory if
necessary, writes to file.
"""
content_path = path
Expand Down
5 changes: 2 additions & 3 deletions staticgenerator/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ def remove(self, path):

def rmdir(self, directory):
os.rmdir(directory)

def join(self, *paths):
if not paths:
return ""
return os.path.join(paths[0], *[path.lstrip("/") for path in paths[1:]])

def dirname(self, path):
return os.path.dirname(path)

1 change: 1 addition & 0 deletions staticgenerator/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.core.handlers.base import BaseHandler


class DummyHandler(BaseHandler):
"""Required to process request and response middleware"""

Expand Down
31 changes: 25 additions & 6 deletions staticgenerator/middleware.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,49 @@
import re
import urllib
from django.conf import settings
from staticgenerator import StaticGenerator


class StaticGeneratorMiddleware(object):
"""
This requires settings.STATIC_GENERATOR_URLS tuple to match on URLs

Example::

STATIC_GENERATOR_URLS = (
r'^/$',
r'^/blog',
)


Sometimes people try stuff like this:

``index.html?page=../../../../../../../../../../proc/self``

STATIC_GENERATOR_QUERYSTRINGS = (
'page',
'print',
)

"""
urls = tuple([re.compile(url) for url in settings.STATIC_GENERATOR_URLS])
querystrings_allowed = tuple([q for q in settings.STATIC_GENERATOR_QUERYSTRINGS])
excluded_urls = tuple([re.compile(url) for url in getattr(settings, 'STATIC_GENERATOR_EXCLUDE_URLS', [])])
gen = StaticGenerator()

def process_response(self, request, response):
path = request.path_info
query_string = request.META.get('QUERY_STRING', '')

if response.status_code == 200:
query_string_dict = request.GET.copy()

if getattr(settings, 'STATIC_GENERATOR_ANONYMOUS_ONLY', False) and not request.user.is_anonymous():
return response

final_querystring = dict()
for key in query_string_dict.keys():
if key in self.querystrings_allowed:
final_querystring[key] = query_string_dict[key]

excluded = False
for url in self.excluded_urls:
if url.match(path):
Expand All @@ -34,7 +53,7 @@ def process_response(self, request, response):
if not excluded:
for url in self.urls:
if url.match(path):
self.gen.publish_from_path(path, query_string, response.content)
self.gen.publish_from_path(path, urllib.urlencode(final_querystring, True), response.content)
break

return response