mirror of
https://github.com/django/django.git
synced 2025-07-05 10:19:20 +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
|
||||
|
||||
class IncludeNode(template.Node):
|
||||
def __init__(self, template_path):
|
||||
def __init__(self, template_path_var):
|
||||
self.template_path_var = template_path_var
|
||||
|
||||
def render(self, context):
|
||||
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)
|
||||
return t.render(context)
|
||||
except:
|
||||
except Exception, e:
|
||||
print e
|
||||
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)
|
||||
this_id = getattr(self, self._meta.pk.column)
|
||||
cursor = db.db.cursor()
|
||||
cursor.execute("DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower()), [this_id])
|
||||
sql = "INSERT INTO %s (%s_id, %s_id) VALUES (%%s, %%s)" % (m2m_table, rel.object_name.lower(), rel_opts.object_name.lower())
|
||||
cursor.executemany(sql, [(this_id, i) for i in id_list])
|
||||
delete_stmt = "DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower())
|
||||
cursor.execute(delete_stmt, [this_id])
|
||||
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()
|
||||
|
||||
# ORDERING METHODS #########################
|
||||
@ -1488,7 +1489,7 @@ def get_manipulator(opts, klass, extra_methods, add=False, change=False):
|
||||
setattr(man, k, v)
|
||||
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:
|
||||
assert obj_key is not None, "ChangeManipulator.__init__() must be passed obj_key parameter."
|
||||
self.obj_key = obj_key
|
||||
|
@ -8,6 +8,12 @@ from django.utils.functional import curry
|
||||
from django.views.admin.main import AdminBoundField
|
||||
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):
|
||||
def __init__(self, var):
|
||||
self.var = var
|
||||
@ -43,23 +49,6 @@ class SubmitRowNode(template.Node):
|
||||
}, context);
|
||||
context.pop()
|
||||
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):
|
||||
def __init__(self, argument):
|
||||
@ -103,20 +92,33 @@ class FieldWidgetNode(template.Node):
|
||||
self.bound_field_var = bound_field_var
|
||||
|
||||
def render(self, context):
|
||||
|
||||
bound_field = template.resolve_variable(self.bound_field_var, context)
|
||||
add = context['add']
|
||||
change = context['change']
|
||||
|
||||
context.push()
|
||||
context['bound_field'] = bound_field
|
||||
t = template_loader.get_template("admin_field_widget")
|
||||
output = t.render(context)
|
||||
klass = bound_field.field.__class__
|
||||
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()
|
||||
|
||||
return output
|
||||
|
||||
|
||||
|
||||
class FieldWrapper(object):
|
||||
def __init__(self, field ):
|
||||
self.field = field
|
||||
@ -259,9 +261,9 @@ one_arg_tag_nodes = [
|
||||
FilterInterfaceScriptMaybeNode,
|
||||
]
|
||||
|
||||
word = re.compile('[A-Z][a-z]+')
|
||||
|
||||
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)
|
||||
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