mirror of
https://github.com/django/django.git
synced 2025-07-06 02:39:12 +00:00
Merge to r764. Fix 684, also allow overloading indivual fields templates in the admin.
git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@765 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
3afc81d44e
commit
cfe2e95529
@ -228,15 +228,17 @@ class SsiNode(template.Node):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
class IncludeNode(template.Node):
|
class IncludeNode(template.Node):
|
||||||
def __init__(self, template_path):
|
def __init__(self, template_path_var):
|
||||||
self.template_path_var = template_path_var
|
self.template_path_var = template_path_var
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
template_path = template.resolve(self.template_path_var, context)
|
template_path = template.resolve_variable(self.template_path_var, context)
|
||||||
|
print "IncludeNode rendering %s" % template_path
|
||||||
t = template_loader.get_template(template_path)
|
t = template_loader.get_template(template_path)
|
||||||
return t.render(context)
|
return t.render(context)
|
||||||
except:
|
except Exception, e:
|
||||||
|
print e
|
||||||
return '' # Fail silently for invalid included templates.
|
return '' # Fail silently for invalid included templates.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1035,9 +1035,10 @@ def method_set_related_many_to_many(rel_opts, rel_field, self, id_list):
|
|||||||
m2m_table = rel_field.get_m2m_db_table(rel_opts)
|
m2m_table = rel_field.get_m2m_db_table(rel_opts)
|
||||||
this_id = getattr(self, self._meta.pk.column)
|
this_id = getattr(self, self._meta.pk.column)
|
||||||
cursor = db.db.cursor()
|
cursor = db.db.cursor()
|
||||||
cursor.execute("DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower()), [this_id])
|
delete_stmt = "DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower())
|
||||||
sql = "INSERT INTO %s (%s_id, %s_id) VALUES (%%s, %%s)" % (m2m_table, rel.object_name.lower(), rel_opts.object_name.lower())
|
cursor.execute(delete_stmt, [this_id])
|
||||||
cursor.executemany(sql, [(this_id, i) for i in id_list])
|
insert_stmt = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % (m2m_table, rel.pk.column, rel_opts.pk.column)
|
||||||
|
cursor.executemany(insert_stmt, [(this_id, i) for i in id_list])
|
||||||
db.db.commit()
|
db.db.commit()
|
||||||
|
|
||||||
# ORDERING METHODS #########################
|
# ORDERING METHODS #########################
|
||||||
@ -1488,7 +1489,7 @@ def get_manipulator(opts, klass, extra_methods, add=False, change=False):
|
|||||||
setattr(man, k, v)
|
setattr(man, k, v)
|
||||||
return man
|
return man
|
||||||
|
|
||||||
def manipulator_init(opts, add, change, self, obj_key=None):
|
def manipulator_init(opts, add, change, self, obj_key=None, follow=None):
|
||||||
if change:
|
if change:
|
||||||
assert obj_key is not None, "ChangeManipulator.__init__() must be passed obj_key parameter."
|
assert obj_key is not None, "ChangeManipulator.__init__() must be passed obj_key parameter."
|
||||||
self.obj_key = obj_key
|
self.obj_key = obj_key
|
||||||
|
@ -8,6 +8,12 @@ from django.utils.functional import curry
|
|||||||
from django.views.admin.main import AdminBoundField
|
from django.views.admin.main import AdminBoundField
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
word_re = re.compile('[A-Z][a-z]+')
|
||||||
|
|
||||||
|
def class_name_to_underscored(name):
|
||||||
|
return '_'.join([ s.lower() for s in word_re.findall(name)[:-1] ])
|
||||||
|
|
||||||
|
|
||||||
class IncludeAdminScriptNode(template.Node):
|
class IncludeAdminScriptNode(template.Node):
|
||||||
def __init__(self, var):
|
def __init__(self, var):
|
||||||
self.var = var
|
self.var = var
|
||||||
@ -43,23 +49,6 @@ class SubmitRowNode(template.Node):
|
|||||||
}, context);
|
}, context);
|
||||||
context.pop()
|
context.pop()
|
||||||
return output;
|
return output;
|
||||||
# t = ['<div class="submit-row">']
|
|
||||||
|
|
||||||
# if not is_popup:
|
|
||||||
# if has_delete_permission and (change or show_delete):
|
|
||||||
# t.append('<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>')
|
|
||||||
# if change and save_as:
|
|
||||||
# t.append('<input type="submit" value="Save as new" name="_saveasnew" %s/>' % onclick_attrib)
|
|
||||||
# if (not save_as or add):
|
|
||||||
# t.append('<input type="submit" value="Save and add another" name="_addanother" %s/>' % onclick_attrib)
|
|
||||||
# t.append('<input type="submit" value="Save and continue editing" name="_continue" %s/>' % onclick_attrib )
|
|
||||||
# t.append('<input type="submit" value="Save" class="default" %s/>' % onclick_attrib)
|
|
||||||
# t.append('</div>\n')
|
|
||||||
|
|
||||||
# return ''.join(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AdminFieldBoundNode(template.Node):
|
class AdminFieldBoundNode(template.Node):
|
||||||
def __init__(self, argument):
|
def __init__(self, argument):
|
||||||
@ -103,20 +92,33 @@ class FieldWidgetNode(template.Node):
|
|||||||
self.bound_field_var = bound_field_var
|
self.bound_field_var = bound_field_var
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
|
|
||||||
bound_field = template.resolve_variable(self.bound_field_var, context)
|
bound_field = template.resolve_variable(self.bound_field_var, context)
|
||||||
add = context['add']
|
add = context['add']
|
||||||
change = context['change']
|
change = context['change']
|
||||||
|
|
||||||
context.push()
|
context.push()
|
||||||
context['bound_field'] = bound_field
|
context['bound_field'] = bound_field
|
||||||
t = template_loader.get_template("admin_field_widget")
|
klass = bound_field.field.__class__
|
||||||
output = t.render(context)
|
t = None
|
||||||
|
while klass:
|
||||||
|
try:
|
||||||
|
field_class_name = klass.__name__
|
||||||
|
template_name = "widget/%s" % \
|
||||||
|
class_name_to_underscored(field_class_name)
|
||||||
|
|
||||||
|
t = template_loader.get_template(template_name)
|
||||||
|
break
|
||||||
|
except template.TemplateDoesNotExist:
|
||||||
|
klass = (len(klass.__bases__) > 0) and klass.__bases__[0] or None
|
||||||
|
|
||||||
|
if t == None:
|
||||||
|
t = template_loader.get_template("widget/default")
|
||||||
|
|
||||||
|
output = t.render(context)
|
||||||
context.pop()
|
context.pop()
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FieldWrapper(object):
|
class FieldWrapper(object):
|
||||||
def __init__(self, field ):
|
def __init__(self, field ):
|
||||||
self.field = field
|
self.field = field
|
||||||
@ -259,9 +261,9 @@ one_arg_tag_nodes = [
|
|||||||
FilterInterfaceScriptMaybeNode,
|
FilterInterfaceScriptMaybeNode,
|
||||||
]
|
]
|
||||||
|
|
||||||
word = re.compile('[A-Z][a-z]+')
|
|
||||||
def register_one_arg_tag(node):
|
def register_one_arg_tag(node):
|
||||||
tag_name = '_'.join([ s.lower() for s in word.findall(node.__name__)[:-1] ])
|
tag_name = class_name_to_underscored(node.__name__)
|
||||||
parse_func = curry(do_one_arg_tag, node)
|
parse_func = curry(do_one_arg_tag, node)
|
||||||
template.register_tag(tag_name, parse_func)
|
template.register_tag(tag_name, parse_func)
|
||||||
|
|
||||||
|
90
docs/outputting_pdf.txt
Normal file
90
docs/outputting_pdf.txt
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
===========================
|
||||||
|
Outputting PDFs with Django
|
||||||
|
===========================
|
||||||
|
|
||||||
|
This document explains how to output PDF files dynamically using Django views.
|
||||||
|
This is made possible by the excellent, open-source ReportLab_ Python PDF
|
||||||
|
library.
|
||||||
|
|
||||||
|
The advantage of generating PDF files dynamically is that you can create
|
||||||
|
customzed PDFs for different purposes -- say, for different users or different
|
||||||
|
pieces of content.
|
||||||
|
|
||||||
|
For example, Django was used at kusports.com to generate customized,
|
||||||
|
printer-friendly NCAA tournament brackets, as PDF files, for people
|
||||||
|
participating in a March Madness contest.
|
||||||
|
|
||||||
|
.. _ReportLab: http://www.reportlab.org/rl_toolkit.html
|
||||||
|
|
||||||
|
Install ReportLab
|
||||||
|
=================
|
||||||
|
|
||||||
|
Download and install the ReportLab library from http://www.reportlab.org/downloads.html.
|
||||||
|
The `user guide`_ (not coincidentally, a PDF file) explains how to install it.
|
||||||
|
|
||||||
|
Test your installation by importing it in the Python interactive interpreter::
|
||||||
|
|
||||||
|
>>> import reportlab
|
||||||
|
|
||||||
|
If that command doesn't raise any errors, the installation worked.
|
||||||
|
|
||||||
|
.. _user guide: http://www.reportlab.org/rsrc/userguide.pdf
|
||||||
|
|
||||||
|
Write your view
|
||||||
|
===============
|
||||||
|
|
||||||
|
The key to generating PDFs dynamically with Django is that the ReportLab API
|
||||||
|
acts on file-like objects, and Django's ``HttpResponse`` objects are file-like
|
||||||
|
objects.
|
||||||
|
|
||||||
|
.. admonition:: Note
|
||||||
|
|
||||||
|
For more information on ``HttpResponse`` objects, see
|
||||||
|
`Request and response objects`_.
|
||||||
|
|
||||||
|
.. _Request and response objects: http://www.djangoproject.com/documentation/request_response/
|
||||||
|
|
||||||
|
Here's a "Hello World" example::
|
||||||
|
|
||||||
|
from reportlab.pdfgen import canvas
|
||||||
|
from django.utils.httpwrappers import HttpResponse
|
||||||
|
|
||||||
|
def some_view(request):
|
||||||
|
# Create the HttpResponse object with the appropriate PDF headers.
|
||||||
|
response = HttpResponse(mimetype='application/pdf')
|
||||||
|
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'
|
||||||
|
|
||||||
|
# Create the PDF object, using the response object as its "file."
|
||||||
|
p = canvas.Canvas(response)
|
||||||
|
|
||||||
|
# Draw things on the PDF. Here's where the PDF generation happens.
|
||||||
|
# See the ReportLab documentation for the full list of functionality.
|
||||||
|
p.drawString(100, 100, "Hello world.")
|
||||||
|
|
||||||
|
# Close the PDF object cleanly, and we're done.
|
||||||
|
p.showPage()
|
||||||
|
p.save()
|
||||||
|
return response
|
||||||
|
|
||||||
|
The code and comments should be self-explanatory, but a few things deserve a
|
||||||
|
mention:
|
||||||
|
|
||||||
|
* The response gets a special mimetype, ``application/pdf``. This tells
|
||||||
|
browsers that the document is a PDF file, rather than an HTML file. If
|
||||||
|
you leave this off, browsers will probably interpret the output as HTML,
|
||||||
|
which would result in ugly, scary gobbledygook in the browser window.
|
||||||
|
|
||||||
|
* The response gets an additional ``Content-Disposition`` header, which
|
||||||
|
contains the name of the PDF file. This filename is arbitrary: Call it
|
||||||
|
whatever you want. It'll be used by browsers in the "Save as..."
|
||||||
|
dialogue, etc.
|
||||||
|
|
||||||
|
* Hooking into the ReportLab API is easy: Just pass ``response`` as the
|
||||||
|
first argument to ``canvas.Canvas``. The ``Canvas`` class expects a
|
||||||
|
file-like object, and ``HttpResponse`` objects fit the bill.
|
||||||
|
|
||||||
|
* Note that all subsequent PDF-generation methods are called on the PDF
|
||||||
|
object (in this case, ``p``) -- not on ``response``.
|
||||||
|
|
||||||
|
* Finally, it's important to call ``showPage()`` and ``save()`` on the PDF
|
||||||
|
file.
|
Loading…
x
Reference in New Issue
Block a user