<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Spoof&#039;s blog &#187; python</title>
	<atom:link href="http://spoofa.info/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://spoofa.info</link>
	<description>...about martian&#039;s and penguin&#039;s life</description>
	<lastBuildDate>Tue, 28 Jul 2009 21:37:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Дружим django и экспорт в Excel</title>
		<link>http://spoofa.info/2008/10/23/%d0%b4%d1%80%d1%83%d0%b6%d0%b8%d0%bc-django-%d0%b8-%d1%8d%d0%ba%d1%81%d0%bf%d0%be%d1%80%d1%82-%d0%b2-excel/</link>
		<comments>http://spoofa.info/2008/10/23/%d0%b4%d1%80%d1%83%d0%b6%d0%b8%d0%bc-django-%d0%b8-%d1%8d%d0%ba%d1%81%d0%bf%d0%be%d1%80%d1%82-%d0%b2-excel/#comments</comments>
		<pubDate>Thu, 23 Oct 2008 17:52:54 +0000</pubDate>
		<dc:creator>spoof</dc:creator>
				<category><![CDATA[comp]]></category>
		<category><![CDATA[devel]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://spoofa.info/?p=95</guid>
		<description><![CDATA[И так, мы хотим, чтобы пользовал клацнув на кнопочку &#8220;Экспорт в Excel&#8221; получил это документ и был доволен. Что может проще скажете вы &#8211; взять положить файл в каталог и тупо его HTTP сервером отдавать. Но не все так просто в случае если вам необходимо, чтобы этот файл генерировался динамически (из-за меняющихся в нем данных), [...]]]></description>
			<content:encoded><![CDATA[<p>И так, мы хотим, чтобы пользовал клацнув на кнопочку &#8220;Экспорт в Excel&#8221; получил это документ и был доволен. Что может проще скажете вы &#8211; взять положить файл в каталог и тупо его HTTP сервером отдавать. Но не все так просто в случае если вам необходимо, чтобы этот файл генерировался динамически (из-за меняющихся в нем данных), например django, на котором крутиться сайт.</p>
<p>Насколько мне удалось узнать, в мире существует один адеватный генератор Excel файлов &#8211; <a href="http://pypi.python.org/pypi/xlwt/">xlwt</a>, который можно юзать на Unix системах. Это клон pyExcelator&#8217;a, но лучше подходит для django и сохранения не в файл. С документацией у него беда, но парочку линков все же нашел: <a href="http://ntalikeris.blogspot.com/2007/10/create-excel-file-with-python-my-sort.html">обзор</a> и <a href="http://files.blog-city.com/files/F05/96843/b/cheatsheet.py">пример</a>.<br />
Кроме того, примеры есть и в тарболе с pyexcelator&#8217;ом или в самом xlwt.</p>
<p>И так создаем во views.py приложения django функцию, которая будет возрвращать xls файл:</p>
<div class="codesnip-container" >def export_xls(request):<br />
wb = Workbook() # создаем основной документ<br />
sheet = wb.add_sheet(&#8217;sheet1&#8242;) # добавляем лист (должен быть как минимум 1 лист)<br />
# далее идут действия по добавлению данный на лист<br />
# это нам не интересно в данном контексте</div>
<p>Но это еще не все. Далее нам нужно этот документ каким-то образом передать пользователю. Отсюда вытекают 2 проблемы или вопроса:</p>
<ol>
<li>Как же собственно преобразовать объект типа <em>Workbook</em> в строку (читай stream). По идее же должен быть какой-то метод save или что-то подобное. Да, есть, но в случае использования pyExcelator&#8217;a метод save предполагает сохранение в файл, но не в <em>переменную</em> как нам надо. В <em>xlwt</em> метод <em>save()</em> более гибкий. Ниже расскажу</li>
<li>И как дать этому &#8220;файлу&#8221; человеческое имя, если у нас данные для генерации xls файла передаются через GET/POST параметры. При таком раскладе у нас файл будет называться последним &#8220;нодом&#8221; url&#8217;a, на который ведет ссылка. Например, если ссылка: http://bla.com/bla/export_xls?bla&amp;bla=2, то файл будет называться export_xls, что не есть гуд</li>
</ol>
<p>Начну пожалуй со второй проблемы, ибо она решается довольно просто. Если залезть в <a href="http://www.ietf.org/rfc/rfc2183.txt">RFC 2183</a>, то можно узнать про интересное поле в HTTP заголовке &#8211; <em>Content-Disposition</em>, которое то и позволит присвоить имя файлу через присвоение ему типа <em>attachment</em> с параметром <em>filename</em>. Для Django это будет выглядеть так:</p>
<div class="codesnip-container" >response = HttpResponse(xls, mimetype=&#8221;application/vnd.ms-excel&#8221;)<br />
response['Content-Disposition'] = &#8216;attachment; filename= %s&#8217; % (filename)<br />
return response<br />
# здесь xls &#8211; фактически представляет собой переменную с содержимым Excel файла,<br />
# filename &#8211; название файла, которое мы должны задать ранее</div>
<p>И так, для решения первой проблемы (в случае если используется pyExcelator) я порылся в коде pyExcelator&#8217;a и выкопал следующее. Оказывается метод <em>save()</em> класса <em>Workbook</em> делает следующее:</p>
<div class="codesnip-container" >def save(self, filename):<br />
import CompoundDoc</div>
<div class="codesnip-container" ></div>
<div class="codesnip-container" >doc = CompoundDoc.XlsDoc()<br />
doc.save(filename, self.get_biff_data())</div>
<p><em>CompoundDoc.XlsDoc()</em> собственно и представляет собой обертку над бинарными данными, который сгенерировал <em>Workbook</em> (их получаем с помощью <em>self.get_biff_data()</em>). Как раз после <em>save()</em> класса<em> CompoundDoc.XlsDoc() </em>и получаем конечный Excel документ. Но смотрим далее в метод <em>save()</em> этого <em>XlsDoc</em>&#8216;a:</p>
<div class="codesnip-container" >def save(self, filename, stream):<br />
# 1. Align stream on 0&#215;1000 boundary (and therefore on sector boundary)<br />
padding = &#8216;\x00&#8242; * (0&#215;1000 &#8211; (len(stream) % 0&#215;1000))<br />
self.book_stream_len = len(stream) + len(padding)</div>
<div class="codesnip-container" >self.__build_directory()<br />
self.__build_sat()<br />
self.__build_header()</div>
<div class="codesnip-container" >f = file(filename, &#8216;wb&#8217;)<br />
f.write(self.header)<br />
f.write(self.packed_MSAT_1st)<br />
f.write(stream)<br />
f.write(padding)<br />
f.write(self.packed_MSAT_2nd)<br />
f.write(self.packed_SAT)<br />
f.write(self.dir_stream)<br />
f.close()</div>
<p>Как видно, опять, тут идет работа с файлов. Слава богу это конечный пункт сохранения файла, поэтому смело овверайдим эти два класса для работы с Django:</p>
<div class="codesnip-container" >class XlsDoc(CompoundDoc.XlsDoc):<br />
def get(self, stream):<br />
padding = &#8216;\x00&#8242; * (0&#215;1000 &#8211; (len(stream) % 0&#215;1000))<br />
self.book_stream_len = len(stream) + len(padding)<br />
self.__build_directory()<br />
self.__build_sat()<br />
self.__build_header()<br />
return &#8216;%s%s%s%s%s%s%s&#8217; % (<br />
self.header,<br />
self.packed_MSAT_1st,<br />
stream,<br />
padding,<br />
self.packed_MSAT_2nd,<br />
self.packed_SAT,<br />
self.dir_stream)</div>
<div class="codesnip-container" ></div>
<div class="codesnip-container" >class Workbook(Workbook):<br />
def get(self):<br />
doc = XlsDoc()<br />
return doc.get(self.get_biff_data())</div>
<p>Вставляем этот код в <em>views.py</em> нашего приложения и в коде нашей функции <em>export_xls()</em> после создания и генерации данных пишем:</p>
<div class="codesnip-container" >xsl = wb.get()</div>
<p>Здесь <em>wb</em> &#8211; это WoorBook из первого листинга (но это не тот Workbook, что из pyExcelator&#8217;a, а наш, который мы переопределили).<br />
Вот собственно и все. Небольшая компиляция собранных с краев интернета кусочков. Может и есть какой-то более прямой способ.<br />
<strong>UPD</strong> В случае использования <em>xlwt</em> все проще. Юзаем:</p>
<div class="codesnip-container" >import cStringIO<br />
xls = cStringIO.StringIO()<br />
..<br />
wb.save(xls)<br />
response = HttpResponse(xls.getvalue(), mimetype=&#8221;application/vnd.ms-excel&#8221;)</div>
]]></content:encoded>
			<wfw:commentRss>http://spoofa.info/2008/10/23/%d0%b4%d1%80%d1%83%d0%b6%d0%b8%d0%bc-django-%d0%b8-%d1%8d%d0%ba%d1%81%d0%bf%d0%be%d1%80%d1%82-%d0%b2-excel/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
