/
views.py
172 lines (143 loc) · 5.46 KB
/
views.py
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
"""
Views that inherit from Django's class-based generic views and add methods
for building flat files.
"""
import os
import six
import shutil
import logging
from django.conf import settings
from django.test.client import RequestFactory
from django.views.generic import TemplateView, DetailView, ListView
logger = logging.getLogger(__name__)
class BuildableTemplateView(TemplateView):
"""
Renders and builds a simple template.
When inherited, the child class should include the following attributes.
build_path:
The target location of the built file in the BUILD_DIR.
`index.html` would place it at the built site's root.
`foo/index.html` would place it inside a subdirectory.
template_name:
The name of the template you would like Django to render.
"""
@property
def build_method(self):
return self.build
def build(self):
logger.debug("Building %s" % self.template_name)
self.request = RequestFactory().get(self.build_path)
html = self.get(self.request).render().content
path = os.path.join(settings.BUILD_DIR, self.build_path)
# Make sure the directory exists
dirname = os.path.dirname(self.build_path)
if dirname:
dirname = os.path.join(settings.BUILD_DIR, dirname)
os.path.exists(dirname) or os.makedirs(dirname)
# Write out the data
outfile = open(path, 'wb')
outfile.write(six.binary_type(html))
outfile.close()
class BuildableListView(ListView):
"""
Render and builds a page about a list of objects.
Required attributes:
model or queryset:
Where the list of objects should come from. `self.queryset` can
be any iterable of items, not just a queryset.
build_path:
The target location of the built file in the BUILD_DIR.
`index.html` would place it at the built site's root.
`foo/index.html` would place it inside a subdirectory.
`index.html is the default.
template_name:
The name of the template you would like Django to render. You need
to override this if you don't want to rely on the Django defaults.
"""
build_path = 'index.html'
@property
def build_method(self):
return self.build_queryset
def build_queryset(self):
logger.debug("Building %s" % self.build_path)
# Make a fake request
self.request = RequestFactory().get(self.build_path)
# Make sure the directory exists
dirname = os.path.dirname(self.build_path)
if dirname:
dirname = os.path.join(settings.BUILD_DIR, dirname)
os.path.exists(dirname) or os.makedirs(dirname)
# Render the list page as HTML
html = self.get(self.request).render().content
# Write it out to the appointed flat file
path = os.path.join(settings.BUILD_DIR, self.build_path)
outfile = open(path, 'wb')
outfile.write(six.binary_type(html))
outfile.close()
class BuildableDetailView(DetailView):
"""
Render and build a "detail" view of an object.
Required attributes:
queryset:
the model instance the objects are looked up from.
template_name:
The name of the template you would like Django to render. You need
to override this if you don't want to rely on the Django defaults.
"""
@property
def build_method(self):
return self.build_queryset
def write(self, path, data):
outfile = open(path, 'wb')
outfile.write(six.binary_type(data))
outfile.close()
def get_url(self, obj):
"""
The URL at which the detail page should appear.
"""
return obj.get_absolute_url()
def get_build_path(self, obj):
"""
Used to determine where to build the detail page. Override this if you
would like your detail page at a different location. By default it
will be built at get_url() + "index.html"
"""
path = os.path.join(settings.BUILD_DIR, self.get_url(obj)[1:])
os.path.exists(path) or os.makedirs(path)
return os.path.join(path, 'index.html')
def set_kwargs(self, obj):
self.kwargs = {
'pk': getattr(obj, 'pk', None),
'slug': getattr(obj, self.get_slug_field(), None),
}
def get_html(self):
"""
How to render the HTML for the detail page. If you choose to render
using something other than a Django template, like HttpResponse for
instance, you will want to override this.
"""
return self.get(self.request).render().content
def build_object(self, obj):
logger.debug("Building %s" % obj)
self.request = RequestFactory().get(self.get_url(obj))
self.set_kwargs(obj)
self.write(
self.get_build_path(obj),
self.get_html()
)
def build_queryset(self):
[self.build_object(o) for o in self.get_queryset().all()]
def unbuild_object(self, obj):
"""
Deletes the directory at self.get_build_path.
"""
logger.debug("Unbuilding %s" % obj)
path = os.path.split(self.get_build_path(obj))[0]
if os.path.exists(path):
shutil.rmtree(path)
class Buildable404View(BuildableTemplateView):
"""
The default Django 404 page, but built out.
"""
build_path = '404.html'
template_name = '404.html'