If you need to create high-quality PDF reports, you can use JasperReports with django. JasperReports is writen in Java, so I wrote a TCP client to connect to a JasperReports server. This is TCP client code which connects to a jasper server listening on port 5000 and is part of your django application: {{{ import settings import socket, pickle class BridgeClient: def __init__(self, f): self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.connect(("127.0.0.1",5000)) packet = pickle.dumps(f) self._sock.send(str(len(packet))+"\n"+packet) size = int(self._readline()) self.raw = self._readbytes(size) self._sock.close() def get_result(self): return (self.raw) def _readline(self): chars = [] while 1: try: char = self._sock.recv(1) except: char = '' if char == '\n': break chars.append(char) return "".join(chars) def _readbytes(self, n): chunks = [] while n > 0: try: chunk = self._sock.recv(n) except: chunk = None if not chunk: raise ServerError("Failed to read response from server") chunks.append(chunk) n -= len(chunk) return "".join(chunks) def get_report(report, params): return report_client.BridgeClient({"report": settings.REPORT_DIR + report, "params": params, }).get_result() }}} You also need jython to run JasperReport TCP server. Here is the code in Jython: {{{ import SocketServer import pickle import org.postgresql.Driver from java.sql.DriverManager import * import sys import os.path import traceback PATH = (os.path.abspath(os.path.dirname(os.path.realpath(__file__)))) sys.path.append(PATH+"/../") import settings from java.util import HashMap; from net.sf.jasperreports.engine import JasperFillManager,JasperExportManager from java.lang import System import java.io.ByteArrayOutputStream import java.io.FileOutputStream from net.sf.jasperreports.engine import JRParameter import java.util.Locale class BridgeRequestHandler(SocketServer.BaseRequestHandler): def handle(self): try: size = int(self._readline()) data = self._readbytes(size) params = pickle.loads(data) host = "" if settings.DATABASE_HOST: if settings.DATABASE_PORT: host = "//%s:%s/" % (settings.DATABASE_HOST, str(settings.DATABASE_PORT)) else: host = "//%s/" % settings.DATABASE_HOST connection = getConnection("jdbc:postgresql:"+ host + settings.DATABASE_NAME, settings.DATABASE_USER, settings.DATABASE_PASSWORD) h = HashMap() for k, v in params["params"].items(): h.put(k,v) h.put(JRParameter.REPORT_LOCALE, java.util.Locale("fa")) report = JasperFillManager.fillReport(params["report"], h, connection) output = java.io.ByteArrayOutputStream() JasperExportManager.exportReportToPdfStream(report, output) output.close() data =output.toByteArray() except: self.request.send("0\n") print traceback.print_exc() else: self.request.send(str(len(data))+"\n") self.request.send(data) def _readline(self): chars = [] while 1: try: char = self.request.recv(1) except: char = '' if not char: self._logException(4, "failed to read response line") raise ServerError("Failed to read server reply header") if char == '\n': break chars.append(char) return "".join(chars) def _readbytes(self, n): chunks = [] while n > 0: try: chunk = self.request.recv(1) except: chunk = None if not chunk: raise ServerError("Failed to read response from server") chunks.append(chunk) n -= len(chunk) return "".join(chunks) #server host is a tuple ('host', port) server = SocketServer.TCPServer(('localhost', 5000), BridgeRequestHandler) server.serve_forever() }}} You need to have these jar files in your classpath: ["itext-1.4.5.jar", "log4j-1.2.jar", "commons-logging.jar", "icu4j.jar", "commons-collections.jar", "commons-digester.jar", "commons-beanutils.jar", "jasperreports-3.0.0.jar"] + JDBC driver to connect to database And this is a sample view {{{ def hello_pdf(request): response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename="hello_world.pdf"' response.write(get_report("hello.jasper", { "title": (u"hello world"), })) return response }}} All codes are under the [BSD license http://code.djangoproject.com/file/django/trunk/LICENSE].