<?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/"
	xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule">

<channel>
	<title>Room 303 &#187; code</title>
	<atom:link href="http://room-303.com/blog/category/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://room-303.com/blog</link>
	<description>Bug to the Feature</description>
	<lastBuildDate>Wed, 08 Sep 2010 16:59:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
<creativeCommons:license>http://creativecommons.org/licenses/by-nc-sa/2.5/pl/</creativeCommons:license>		<item>
		<title>See no eval, hear no eval, speak no eval</title>
		<link>http://room-303.com/blog/2010/09/03/see-no-eval-hear-no-eval-speak-no-eval/</link>
		<comments>http://room-303.com/blog/2010/09/03/see-no-eval-hear-no-eval-speak-no-eval/#comments</comments>
		<pubDate>Fri, 03 Sep 2010 15:51:31 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=1060</guid>
		<description><![CDATA[Choć sam konta w Książeczce Maryja nie posiadam, przyszło mi ostatnio ścierać się z ichnim API, coby dla klienta wdrożenia poczynić. Po przejrzeniu dostępnych bibliotek (w tym dość żałosnego python-sdk), stanęło na dość popularnym projekcie pyfacebook. Oryginalne repozytorium było wybrakowane pod względem funkcjonalności, wybraliśmy więc fork, który z pewnością stworzył fan Władcy Pierścieni. Jak się [...]]]></description>
			<content:encoded><![CDATA[<p>Choć sam konta w <a href="http://www.facebook.com/">Książeczce Maryja</a> nie posiadam, przyszło mi ostatnio ścierać się z ichnim API, coby dla klienta wdrożenia poczynić. Po przejrzeniu dostępnych bibliotek (w tym dość żałosnego <a href="http://github.com/facebook/python-sdk/">python-sdk</a>), stanęło na dość popularnym projekcie <em>pyfacebook</em>.</p>
<p>Oryginalne repozytorium było wybrakowane pod względem funkcjonalności, wybraliśmy więc fork, który z pewnością stworzył fan Władcy Pierścieni. Jak się miało później okazać, „jeden, by wszystkie zgromadzić i w ciemności związać” dość wiernie oddaje uzyskany efekt.</p>
<p>Człowiek głupi, to przy pierwszym problemie zajrzał w bebechy ofiary. Po tym stanął jak wryty i całe Satchmo przeleciało mu przed oczami. Na początek tradycyjny problem — przejście się w glanach po separacji warstw:</p>
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">local</span>

<span class="n">_thread_locals</span> <span class="o">=</span> <span class="n">local</span><span class="p">()</span>
</pre>
</div>
<div class="highlight">
<pre><span class="k">def</span> <span class="nf">get_facebook_client</span><span class="p">():</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Get the current Facebook object for the calling thread.</span>

<span class="sd">    &quot;&quot;&quot;</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">_thread_locals</span><span class="o">.</span><span class="n">facebook</span>
    <span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
        <span class="k">raise</span> <span class="n">ImproperlyConfigured</span><span class="p">(</span><span class="s">&#39;Make sure you have the Facebook middleware installed.&#39;</span><span class="p">)</span>
</pre>
</div>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">FacebookMiddleware</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Middleware that attaches a Facebook object to every incoming request.</span>
<span class="sd">    The Facebook object created can also be accessed from models for the</span>
<span class="sd">    current thread by using get_facebook_client().</span>

<span class="sd">    callback_path can be a string or a callable.  Using a callable lets us</span>
<span class="sd">    pass in something like lambda reverse(&#39;our_canvas_view&#39;) so we can follow</span>
<span class="sd">    the DRY principle.</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="c"># ...</span>

    <span class="k">def</span> <span class="nf">process_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
        <span class="c"># ...</span>
        <span class="n">_thread_locals</span><span class="o">.</span><span class="n">facebook</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">facebook</span> <span class="o">=</span> <span class="n">Facebook</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">api_key</span><span class="p">,</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">secret_key</span><span class="p">,</span> <span class="n">app_name</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">app_name</span><span class="p">,</span>
                <span class="n">callback_path</span><span class="o">=</span><span class="n">callback_path</span><span class="p">,</span> <span class="n">internal</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">internal</span><span class="p">,</span>
                <span class="n">proxy</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">proxy</span><span class="p">,</span> <span class="n">app_id</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">app_id</span><span class="p">,</span> <span class="n">oauth2</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">oauth2</span><span class="p">)</span>
</pre>
</div>
<p>Przerażające konstrukcje zaczęły się jednak później:</p>
<div class="highlight">
<pre><span class="c"># generate the Facebook proxies</span>
<span class="k">def</span> <span class="nf">__generate_proxies</span><span class="p">():</span>
    <span class="k">for</span> <span class="n">namespace</span> <span class="ow">in</span> <span class="n">METHODS</span><span class="p">:</span>
        <span class="n">methods</span> <span class="o">=</span> <span class="p">{}</span>

        <span class="k">for</span> <span class="n">method</span><span class="p">,</span> <span class="n">param_data</span> <span class="ow">in</span> <span class="n">METHODS</span><span class="p">[</span><span class="n">namespace</span><span class="p">]</span><span class="o">.</span><span class="n">iteritems</span><span class="p">():</span>
            <span class="n">methods</span><span class="p">[</span><span class="n">method</span><span class="p">]</span> <span class="o">=</span> <span class="n">__generate_facebook_method</span><span class="p">(</span><span class="n">namespace</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="n">param_data</span><span class="p">)</span>

        <span class="n">proxy</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s">&#39;</span><span class="si">%s</span><span class="s">Proxy&#39;</span> <span class="o">%</span> <span class="n">namespace</span><span class="o">.</span><span class="n">title</span><span class="p">(),</span> <span class="p">(</span><span class="n">Proxy</span><span class="p">,),</span> <span class="n">methods</span><span class="p">)</span>

        <span class="nb">globals</span><span class="p">()[</span><span class="n">proxy</span><span class="o">.</span><span class="n">__name__</span><span class="p">]</span> <span class="o">=</span> <span class="n">proxy</span>

<span class="n">__generate_proxies</span><span class="p">()</span>
</pre>
</div>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">Facebook</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Provides access to the Facebook API.</span>

<span class="sd">    ...</span>
<span class="sd">    &quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">api_key</span><span class="p">,</span> <span class="n">secret_key</span><span class="p">,</span> <span class="n">auth_token</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">app_name</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                 <span class="n">callback_path</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">internal</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">proxy</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                 <span class="n">facebook_url</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">facebook_secure_url</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                 <span class="n">generate_session_secret</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">app_id</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">oauth2</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
        <span class="c"># ...</span>
        <span class="k">for</span> <span class="n">namespace</span> <span class="ow">in</span> <span class="n">METHODS</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">__dict__</span><span class="p">[</span><span class="n">namespace</span><span class="p">]</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="s">&#39;</span><span class="si">%s</span><span class="s">Proxy(self, </span><span class="se">&#39;</span><span class="si">%s</span><span class="se">&#39;</span><span class="s">)&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">namespace</span><span class="o">.</span><span class="n">title</span><span class="p">(),</span> <span class="s">&#39;facebook.</span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="n">namespace</span><span class="p">))</span>
</pre>
</div>
<p>Skończyło się na własnym forku i refaktoryzacji tych i wielu innych fragmentów kodu. Poprawioną wersję można znaleźć <a href="http://github.com/patrys/pyfacebook">na GitHubie</a>.</p>
<p>Na koniec stare powiedzenie ludowe:</p>
<blockquote><p>Gdy bowiem zoczysz, iż jest coś narzeczy, a za cwanego masz się i uważasz <code>__globals__</code> i <code>eval()</code> za sprawy rozwiązanie, mylisz się wielce, przeto idź przypudrować nos¹.</p>
</blockquote>
<p><small>¹ <cite><a href="http://pl.wikiquote.org/wiki/Cia%C5%82o_(film)">Ciało</a></cite>, 2003</small></p>
<p><small><a href="http://room-303.com/blog/2010/09/03/see-no-eval-hear-no-eval-speak-no-eval/">See no eval, hear no eval, speak no eval</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/09/03/see-no-eval-hear-no-eval-speak-no-eval/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Python: Nie będziesz używał **kwargs nadaremnie</title>
		<link>http://room-303.com/blog/2010/08/04/python-nie-bedziesz-uzywal-kwargs-nadaremnie/</link>
		<comments>http://room-303.com/blog/2010/08/04/python-nie-bedziesz-uzywal-kwargs-nadaremnie/#comments</comments>
		<pubDate>Wed, 04 Aug 2010 14:16:52 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=1001</guid>
		<description><![CDATA[Prawidłowe użycie: def foo(bar, *args, **kwargs): frobnicate(bar) baz(*args, **kwargs) Nigdy tak¹: def foo(bar, *args, **kwargs): frobnicate(bar) baz() ¹ Z wyjątkiem kilku API, które wyraźnie rezerwują sobie możliwość wprowadzenia dodatkowych parametrów. Działają tak np. wbudowane sygnały w Django. Ciche połykanie śmieci w parametrach w malowniczy sposób zemści się przy pierwszej literówce w kodzie. I będzie ci [...]]]></description>
			<content:encoded><![CDATA[<p>Prawidłowe użycie:</p>
<div class="highlight">
<pre><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">bar</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="n">frobnicate</span><span class="p">(</span><span class="n">bar</span><span class="p">)</span>
    <span class="n">baz</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre>
</div>
<p>Nigdy tak¹:</p>
<div class="highlight">
<pre><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">bar</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="n">frobnicate</span><span class="p">(</span><span class="n">bar</span><span class="p">)</span>
    <span class="n">baz</span><span class="p">()</span>
</pre>
</div>
<p><small>¹  Z wyjątkiem kilku API, które wyraźnie rezerwują sobie możliwość wprowadzenia dodatkowych parametrów. Działają tak np. wbudowane sygnały w Django.</small></p>
<p>Ciche połykanie śmieci w parametrach w malowniczy sposób zemści się przy pierwszej literówce w kodzie. I będzie ci się należało.</p>
<p>Jeśli wydaje ci się, że w ten sposób skracasz sobie kod, to przypomnij sobie starą zasadę: utrzymywanie kodu wymaga dwa razy więcej inteligencji i sprytu, niż jego napisanie. Jeśli cały swój spryt włożysz w stworzenie kodu, to z definicji jesteś za głupi, by go potem debugować.</p>
<p><small><a href="http://room-303.com/blog/2010/08/04/python-nie-bedziesz-uzywal-kwargs-nadaremnie/">Python: Nie będziesz używał **kwargs nadaremnie</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/08/04/python-nie-bedziesz-uzywal-kwargs-nadaremnie/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>To jest mój wątek i nie oddam go nikomu</title>
		<link>http://room-303.com/blog/2010/07/26/to-jest-moj-watek-i-nie-oddam-go-nikomu/</link>
		<comments>http://room-303.com/blog/2010/07/26/to-jest-moj-watek-i-nie-oddam-go-nikomu/#comments</comments>
		<pubDate>Mon, 26 Jul 2010 14:53:26 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[satchmo]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=991</guid>
		<description><![CDATA[Bezstanowość protokołu HTTP jest faktem. Niezależnie od tego, czego chciałby autor danej aplikacji. Na przykład nie jest prawdą, że jeden proces lub wątek serwera jest przypisany jednemu, konkretnemu odwiedzającemu stronę. Wybaczcie zatem łzy, które zakręciły mi się w oczach na widok tego: def _get_taxprocessor(request=None): taxprocessor = get_thread_variable(&#39;taxer&#39;, None) if not taxprocessor: if request: user = [...]]]></description>
			<content:encoded><![CDATA[<p>Bezstanowość protokołu HTTP jest faktem. Niezależnie od tego, czego chciałby autor danej aplikacji. Na przykład nie jest prawdą, że jeden proces lub wątek serwera jest przypisany jednemu, konkretnemu odwiedzającemu stronę. Wybaczcie zatem łzy, które zakręciły mi się w oczach na widok tego:</p>
<div class="highlight">
<pre><span class="k">def</span> <span class="nf">_get_taxprocessor</span><span class="p">(</span><span class="n">request</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">taxprocessor</span> <span class="o">=</span> <span class="n">get_thread_variable</span><span class="p">(</span><span class="s">&#39;taxer&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">taxprocessor</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">request</span><span class="p">:</span>
            <span class="n">user</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span>
            <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">is_authenticated</span><span class="p">():</span>
                <span class="n">user</span> <span class="o">=</span> <span class="n">user</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">user</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">user</span> <span class="o">=</span> <span class="n">get_current_user</span><span class="p">()</span>

        <span class="n">taxprocessor</span> <span class="o">=</span> <span class="n">get_tax_processor</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="n">user</span><span class="p">)</span>
        <span class="n">set_thread_variable</span><span class="p">(</span><span class="s">&#39;taxer&#39;</span><span class="p">,</span> <span class="n">taxprocessor</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">taxprocessor</span>
</pre>
</div>
<p>Dalej był już tylko płacz:</p>
<div class="highlight">
<pre><span class="k">def</span> <span class="nf">get_current_user</span><span class="p">():</span>
    <span class="n">user</span> <span class="o">=</span> <span class="n">get_thread_variable</span><span class="p">(</span><span class="s">&#39;user&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">user</span> <span class="o">!=</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="n">user</span>
    <span class="n">req</span> <span class="o">=</span> <span class="n">get_current_request</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">req</span> <span class="o">==</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">None</span>
    <span class="k">return</span> <span class="n">req</span><span class="o">.</span><span class="n">user</span>
</pre>
</div>
<p>I zgrzytanie zębów:</p>
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">local</span>

<span class="n">_threadlocals</span> <span class="o">=</span> <span class="n">local</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">set_current_user</span><span class="p">(</span><span class="n">user</span><span class="p">):</span>
    <span class="n">set_thread_variable</span><span class="p">(</span><span class="s">&#39;user&#39;</span><span class="p">,</span> <span class="n">user</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">set_thread_variable</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">var</span><span class="p">):</span>
    <span class="nb">setattr</span><span class="p">(</span><span class="n">_threadlocals</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">var</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_thread_variable</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">_threadlocals</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_current_request</span><span class="p">():</span>
    <span class="k">return</span> <span class="n">get_thread_variable</span><span class="p">(</span><span class="s">&#39;request&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
</pre>
</div>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">ThreadLocalMiddleware</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;Middleware that gets various objects from the</span>
<span class="sd">    request object and saves them in thread local storage.&quot;&quot;&quot;</span>

    <span class="k">def</span> <span class="nf">process_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
        <span class="n">set_thread_variable</span><span class="p">(</span><span class="s">&#39;request&#39;</span><span class="p">,</span> <span class="n">request</span><span class="p">)</span>
        <span class="n">set_current_user</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>
</pre>
</div>
<p>Pytanie-zagadka: co się stanie z podatkiem, gdy ten sam wątek serwera obsłuży innego użytkownika? Pytanie pomocnicze: skąd może pochodzić ów kod?</p>
<p>Otrzymujesz <em>k3</em> Punkty Obłędu. Jeśli całkowita liczba zebranych punktów wynosi 6 lub więcej, rozpatrz test nabycia trwałej choroby psychicznej zgodnie z procedurą opisaną w rozdziale <em>Obłęd</em> podręcznika.</p>
<p><small><a href="http://room-303.com/blog/2010/07/26/to-jest-moj-watek-i-nie-oddam-go-nikomu/">To jest mój wątek i nie oddam go nikomu</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/07/26/to-jest-moj-watek-i-nie-oddam-go-nikomu/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Być jak Satchmo</title>
		<link>http://room-303.com/blog/2010/07/20/byc-jak-satchmo/</link>
		<comments>http://room-303.com/blog/2010/07/20/byc-jak-satchmo/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 15:20:30 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[satchmo]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=974</guid>
		<description><![CDATA[Z pewnością naczytałeś się już, jakie to Satchmo nie jest doskonałe, zazdrościsz i chciałbyś stworzyć coś równie wspaniałego. Dość jednak nieprzespanych nocy, albowiem przygotowałem krótki poradnik, który w kilku krokach pozwoli ci dorównać mistrzom. Sięgaj tam, gdzie import nie sięga Tak jest, zacznij od stworzenia modułu z myślnikiem w nazwie. Niestety, oczywista nazwa email-auth.py została [...]]]></description>
			<content:encoded><![CDATA[<p>Z pewnością naczytałeś się już, jakie to <a href="http://room-303.com/blog/2010/04/18/django-pinax-i-satchmo/">Satchmo nie jest doskonałe</a>, zazdrościsz i chciałbyś stworzyć coś równie wspaniałego. Dość jednak nieprzespanych nocy, albowiem przygotowałem krótki poradnik, który w kilku krokach pozwoli ci dorównać mistrzom.</p>
<h3>Sięgaj tam, gdzie import nie sięga</h3>
<p>Tak jest, zacznij od stworzenia modułu z myślnikiem w nazwie. Niestety, oczywista nazwa <code>email-auth.py</code> została już zajęta — musisz się bardziej wysilić. Znajomym oczy wypadną z zazdrości, gdy tylko pierwszy raz zobaczą:</p>
<div class="highlight">
<pre><span class="n">hahaha</span> <span class="o">=</span> <span class="nb">__import__</span><span class="p">(</span><span class="s">&#39;pokonalem-was&#39;</span><span class="p">,</span> <span class="nb">globals</span><span class="p">(),</span> <span class="nb">locals</span><span class="p">(),</span> <span class="p">[],</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</pre>
</div>
<h3>Uatrakcyjniaj pętle</h3>
<p>Od dawna wiadomo już, że przedwczesna optymalizacja jest złem, naszą odpowiedzią będzie zatem <q>przedwczesna dezoptymalizacja</q>! Oto przykład atrakcyjnego wyświetlenia listy:</p>
<div class="highlight">
<pre><span class="cp">{%</span> <span class="k">for</span> <span class="nv">product</span> <span class="k">in</span> <span class="nv">products</span> <span class="cp">%}</span>
    <span class="cp">{%</span> <span class="k">if</span> <span class="nb">forloop</span><span class="nv">.first</span> <span class="cp">%}</span> <span class="nt">&lt;ul&gt;</span>  <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
        <span class="nt">&lt;li&gt;</span><span class="cp">{%</span> <span class="k">thumbnail</span> <span class="nv">product.main_image.picture</span> <span class="m">85</span><span class="nv">x85</span> <span class="k">as</span> <span class="nv">image</span> <span class="cp">%}</span>
        <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">product.get_absolute_url</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;&lt;img</span> <span class="na">src=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">image</span> <span class="cp">}}</span><span class="s">&quot;</span> <span class="na">width=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">image.width</span> <span class="cp">}}</span><span class="s">&quot;</span> <span class="na">height=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">image.height</span> <span class="cp">}}</span><span class="s">&quot;</span> <span class="nt">/&gt;&lt;/a&gt;</span>
        <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">product.get_absolute_url</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span><span class="cp">{{</span> <span class="nv">product.translated_name</span> <span class="cp">}}</span><span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
    <span class="cp">{%</span> <span class="k">if</span> <span class="nb">forloop</span><span class="nv">.last</span> <span class="cp">%}</span> <span class="nt">&lt;/ul&gt;</span> <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
</pre>
</div>
<p>Tylko wyobraź sobie ich miny! Jeśli chcesz przeskoczyć mistrza, spróbuj przenieść kod do Pythona:</p>
<div class="highlight">
<pre><span class="k">for</span> <span class="n">counter</span><span class="p">,</span> <span class="n">product</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">products</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">print</span> <span class="s">&#39;&lt;ul&gt;&#39;</span>
    <span class="c"># ...i tak dalej</span>
</pre>
</div>
<h3>Nie daj się zaszufladkować</h3>
<p>Nie łudźmy się — przestrzenie nazw są dla frajerów. Bez ceregieli pakuj wszystko w ścieżkę Pythona i upewnij się, że tak właśnie importujesz swoje moduły. Pokaż, że jesteś ważniakiem i twórz moduły o jak najogólniejszych nazwach. Naucz fajansów pokory, tych kilka dodatkowych wpisów w <code>PYTHONPATH</code> ich nie zabije. Mogą to zrobić konflikty, ale jeśli chcą używać czegoś ponad twój framework, to sami są sobie winni i zasłużyli na karę.</p>
<div class="highlight">
<pre><span class="go">export PYTHONPATH=~/web/satchmo/satchmo/apps</span>
</pre>
</div>
<h3>Unikaj biurokracji</h3>
<p>Po co męczyć się z formularzami, gdy do wszystkiego sięgnąć można już w widoku? Niezwykle istotne jest tu unikanie <code>request.REQUEST</code>.</p>
<div class="highlight">
<pre><span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span><span class="o">==</span><span class="s">&quot;POST&quot;</span><span class="p">:</span>
    <span class="n">reqdata</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">POST</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">reqdata</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span>

<span class="k">if</span> <span class="n">reqdata</span><span class="o">.</span><span class="n">has_key</span><span class="p">(</span><span class="s">&#39;quantity&#39;</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">quantity</span> <span class="o">=</span> <span class="n">round_decimal</span><span class="p">(</span><span class="n">reqdata</span><span class="p">[</span><span class="s">&#39;quantity&#39;</span><span class="p">],</span> <span class="n">places</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">roundfactor</span><span class="o">=.</span><span class="mi">25</span><span class="p">)</span>
    <span class="k">except</span> <span class="n">RoundedDecimalError</span><span class="p">:</span>
        <span class="n">quantity</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;1.0&#39;</span><span class="p">)</span>
        <span class="n">log</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s">&quot;Could not parse a decimal from &#39;</span><span class="si">%s</span><span class="s">&#39;, returning &#39;1.0&#39;&quot;</span><span class="p">,</span> <span class="n">reqdata</span><span class="p">[</span><span class="s">&#39;quantity&#39;</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">quantity</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;1.0&#39;</span><span class="p">)</span>
</pre>
</div>
<p>Jeśli już musisz użyć formularza, upewnij się, że upakujesz wszystkie, niezwiązane ze sobą grupy pól w jednej dużej klasie. Dzięki temu zaoszczędzisz sobie kilka wywołań <code>is_valid()</code> i jednocześnie udaremnisz wszelkie próby innego wykorzystania poszczególnych części przez te niedorozwoje, które mają czelność importować twoje klasy.</p>
<h3>Wyznaczaj nowe trendy</h3>
<p>Przez takich jak oni, programowanie obiektowe stoi praktycznie w miejscu. Pokaż im nowe sztuczki, takie jak zastąpienie rozszerzania klas nadpisywaniem funkcji w locie¹:</p>
<div class="highlight">
<pre><span class="k">def</span> <span class="nf">confirm_info</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">template</span><span class="o">=</span><span class="s">&#39;shop/checkout/protx/confirm.html&#39;</span><span class="p">,</span> <span class="n">extra_context</span><span class="o">=</span><span class="p">{}):</span>
    <span class="n">payment_module</span> <span class="o">=</span> <span class="n">config_get_group</span><span class="p">(</span><span class="s">&#39;PAYMENT_PROTX&#39;</span><span class="p">)</span>
    <span class="n">controller</span> <span class="o">=</span> <span class="n">confirm</span><span class="o">.</span><span class="n">ConfirmController</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">payment_module</span><span class="p">)</span>
    <span class="n">controller</span><span class="o">.</span><span class="n">templates</span><span class="p">[</span><span class="s">&#39;CONFIRM&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">template</span>
    <span class="n">controller</span><span class="o">.</span><span class="n">extra_context</span> <span class="o">=</span> <span class="n">extra_context</span>
    <span class="n">controller</span><span class="o">.</span><span class="n">onForm</span> <span class="o">=</span> <span class="n">secure3d_form_handler</span>
    <span class="n">controller</span><span class="o">.</span><span class="n">confirm</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">controller</span><span class="o">.</span><span class="n">response</span>
</pre>
</div>
<p><small>¹ W rzeczywistości <code>ConfirmController.onForm</code> jest w konstruktorze klasy ustawiany na <code>ConfirmController._onForm</code>, co można uznać za architekturę po dwakroć lepszą.</small></p>
<h3>Parametry dobieraj z rozmachem</h3>
<p>Piękno tkwi w różnorodności. Upewnij się zatem, że wyczerpiesz wszelkie metody osiągnięcia tego samego celu.</p>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">ProductImage</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
    <span class="c"># ...</span>
    <span class="n">picture</span> <span class="o">=</span> <span class="n">ImageWithThumbnailField</span><span class="p">(</span><span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&#39;Picture&#39;</span><span class="p">),</span>
        <span class="n">upload_to</span><span class="o">=</span><span class="s">&quot;__DYNAMIC__&quot;</span><span class="p">,</span>
        <span class="n">name_field</span><span class="o">=</span><span class="s">&quot;_filename&quot;</span><span class="p">,</span>
        <span class="n">max_length</span><span class="o">=</span><span class="mi">200</span><span class="p">)</span>
    <span class="c"># ...</span>

<span class="k">class</span> <span class="nc">ImageWithThumbnailField</span><span class="p">(</span><span class="n">ImageField</span><span class="p">):</span>
    <span class="c"># ...</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                 <span class="n">width_field</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">height_field</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                 <span class="n">auto_rename</span><span class="o">=</span><span class="n">NOTSET</span><span class="p">,</span> <span class="n">name_field</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                 <span class="n">upload_to</span><span class="o">=</span><span class="n">upload_dir</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="c"># ...</span>
        <span class="k">if</span> <span class="n">upload_to</span> <span class="o">==</span> <span class="s">&quot;__DYNAMIC__&quot;</span><span class="p">:</span>
            <span class="n">upload_to</span> <span class="o">=</span> <span class="n">upload_dir</span>
        <span class="c"># ...</span>
</pre>
</div>
<h3>Bądź elastyczny</h3>
<p>Teraz twój sklep znajduje się w Polsce, ale kto wie, co będzie <em>po</em> obiedzie? Upewnij się, że cała konfiguracja może być edytowana w locie. Zwłaszcza te jej fragmenty, które wymagają restartu aplikacji. To jedna z wielu sztuczek, które zapewnią ci popularność w branży. Co prawda dawni przyjaciele zazdroszczą ci już do tego stopnia, że przestali się do ciebie odzywać, ale i tak nie tęsknisz po tych prostakach. Od teraz twoim jedynym przyjacielem jest aplikacja <a href="http://bitbucket.org/bkroeze/django-livesettings/">django-livesettings</a>. Na innych nie masz szans, bo przyjaciół poznaje się w biedzie, a ty przecież właśnie zyskałeś umiejętności, dzięki którym praktycznie już jesteś bogaty.</p>
<p><small><a href="http://room-303.com/blog/2010/07/20/byc-jak-satchmo/">Być jak Satchmo</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/07/20/byc-jak-satchmo/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Python: finanse</title>
		<link>http://room-303.com/blog/2010/05/20/python-finanse/</link>
		<comments>http://room-303.com/blog/2010/05/20/python-finanse/#comments</comments>
		<pubDate>Thu, 20 May 2010 20:36:56 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=931</guid>
		<description><![CDATA[Nie dalej jak wczoraj kolega podesłał mi łatkę do mojej biblioteki do słownego zapisu liczb i kwot. Nie zdradzę od kogo, by chronić niewinnego. Grunt, że łatka wyglądała tak: --- to_words_pl.py (upstream) +++ to_words_pl.py (working copy) @@ -82,7 +82,7 @@ iteration += 1 if unit: result.append(unit) - result.append(u&#39;%d/100&#39; % int(remainder * 100)) + result.append(u&#39;%d/100&#39; % [...]]]></description>
			<content:encoded><![CDATA[<p>Nie dalej jak wczoraj kolega podesłał mi łatkę do mojej <a href="http://github.com/patrys/python-number-to-words-pl">biblioteki do słownego zapisu liczb i kwot</a>. Nie zdradzę od kogo, by chronić niewinnego. Grunt, że łatka wyglądała tak:</p>
<div class="highlight">
<pre><span class="gd">--- to_words_pl.py	(upstream)</span>
<span class="gi">+++ to_words_pl.py	(working copy)</span>
<span class="gu">@@ -82,7 +82,7 @@</span>
         iteration += 1
     if unit:
         result.append(unit)
<span class="gd">-    result.append(u&#39;%d/100&#39; % int(remainder * 100))</span>
<span class="gi">+    result.append(u&#39;%d/100&#39; % int(round(remainder * 100)))</span>
     result = &#39; &#39;.join(result)
     return result
</pre>
</div>
<p>Zdziwiłem się bardzo, bo zwykłem kwoty odpowiednio zaokrąglać do dwóch miejsc po przecinku. Co się jednak okazało? <code>0.48</code> zamieniało się w <code>0.47</code>. A dokładniej? W <code>0.47999999999999998</code>. Tuś mi, ptaszku.</p>
<p>Patrząc na <code>0.48</code> tak naprawdę w głowie widziałem <code>decimal.Decimal('0.48')</code>. Jak się jednak okazuje, niektórzy próbują operacje finansowe przeprowadzać na liczbach zmiennopozycyjnych. <strong>Nie używamy typu <code>float</code> do operacji finansowych.</strong> Dlaczego?</p>
<div class="highlight">
<pre><span class="gp">&gt;&gt;&gt; </span><span class="mf">0.48</span>
<span class="go">0.47999999999999998</span>
<span class="gp">&gt;&gt;&gt; </span><span class="mf">0.82</span>
<span class="go">0.81999999999999995</span>
</pre>
</div>
<p>Do operacji na liczbach o znanej precyzji używamy typu <code>decimal.Decimal</code> i jego kontrolowanego (i konfigurowalnego!) mechanizmu zaokrąglania:</p>
<div class="highlight">
<pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">decimal</span> <span class="kn">import</span> <span class="n">Decimal</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;0.48&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;0.12&#39;</span><span class="p">)</span>
<span class="go">Decimal(&#39;0.60&#39;)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">vat</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;0.48&#39;</span><span class="p">)</span> <span class="o">*</span> <span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;0.22&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">vat</span><span class="o">.</span><span class="n">quantize</span><span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="s">&#39;0.01&#39;</span><span class="p">))</span>
<span class="go">Decimal(&#39;0.11&#39;)</span>
</pre>
</div>
<p><small><a href="http://room-303.com/blog/2010/05/20/python-finanse/">Python: finanse</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/05/20/python-finanse/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Python: wyjątkowo głupi pomysł</title>
		<link>http://room-303.com/blog/2010/05/13/python-wyjatkowo-glupi-pomysl/</link>
		<comments>http://room-303.com/blog/2010/05/13/python-wyjatkowo-glupi-pomysl/#comments</comments>
		<pubDate>Thu, 13 May 2010 16:12:38 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=926</guid>
		<description><![CDATA[Nigdy nie róbcie tak: try: # ... except FooException, e: # ... raise e Wyjątki przepuszcza się tak: try: # ... except FooException: # ... raise Istotna różnica polega na niezniszczeniu całego stosu wywołań. Z góry dziękuję. Python: wyjątkowo głupi pomysł © Patryk Zawadzki, Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland.]]></description>
			<content:encoded><![CDATA[<p>Nigdy nie róbcie tak:</p>
<div class="highlight">
<pre><span class="k">try</span><span class="p">:</span>
    <span class="c"># ...</span>
<span class="k">except</span> <span class="n">FooException</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
    <span class="c"># ...</span>
    <span class="k">raise</span> <span class="n">e</span>
</pre>
</div>
<p>Wyjątki przepuszcza się tak:</p>
<div class="highlight">
<pre><span class="k">try</span><span class="p">:</span>
    <span class="c"># ...</span>
<span class="k">except</span> <span class="n">FooException</span><span class="p">:</span>
    <span class="c"># ...</span>
    <span class="k">raise</span>
</pre>
</div>
<p>Istotna różnica polega na niezniszczeniu całego stosu wywołań. Z góry dziękuję.</p>
<p><small><a href="http://room-303.com/blog/2010/05/13/python-wyjatkowo-glupi-pomysl/">Python: wyjątkowo głupi pomysł</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/05/13/python-wyjatkowo-glupi-pomysl/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Django: abstrakcji ciąg dalszy</title>
		<link>http://room-303.com/blog/2010/04/27/django-abstrakcji-ciag-dalszy/</link>
		<comments>http://room-303.com/blog/2010/04/27/django-abstrakcji-ciag-dalszy/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 09:33:38 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=901</guid>
		<description><![CDATA[Tym razem inne podejście, naturalne dziedziczenie abstrakcyjnych modeli z dwoma dodatkowymi metodami. W przeciwieństwie do poprzedniego przykładu, pozwala używać super() w abstrakcyjnych klasach pośrednich. from django.db import models class AbstractMixin(object): _classcache = {} @classmethod def contribute(cls): return {} @classmethod def construct(cls, *args, **kwargs): attrs = cls.contribute(*args, **kwargs) attrs.update({ &#39;__module__&#39;: cls.__module__, &#39;Meta&#39;: type(&#39;Meta&#39;, (), {&#39;abstract&#39;: True}), [...]]]></description>
			<content:encoded><![CDATA[<p>Tym razem inne podejście, naturalne dziedziczenie abstrakcyjnych modeli z dwoma dodatkowymi metodami.</p>
<p>W przeciwieństwie do <a href="http://room-303.com/blog/2010/04/22/django-zabawa-z-abstrakcja/">poprzedniego przykładu</a>, pozwala używać <code>super()</code> w abstrakcyjnych klasach pośrednich.</p>
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">AbstractMixin</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="n">_classcache</span> <span class="o">=</span> <span class="p">{}</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">contribute</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
        <span class="k">return</span> <span class="p">{}</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">construct</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">attrs</span> <span class="o">=</span> <span class="n">cls</span><span class="o">.</span><span class="n">contribute</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="n">attrs</span><span class="o">.</span><span class="n">update</span><span class="p">({</span>
            <span class="s">&#39;__module__&#39;</span><span class="p">:</span> <span class="n">cls</span><span class="o">.</span><span class="n">__module__</span><span class="p">,</span>
            <span class="s">&#39;Meta&#39;</span><span class="p">:</span> <span class="nb">type</span><span class="p">(</span><span class="s">&#39;Meta&#39;</span><span class="p">,</span> <span class="p">(),</span> <span class="p">{</span><span class="s">&#39;abstract&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">}),</span>
        <span class="p">})</span>
        <span class="n">key</span> <span class="o">=</span> <span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">kwargs</span><span class="o">.</span><span class="n">items</span><span class="p">()))</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cls</span><span class="o">.</span><span class="n">_classcache</span><span class="p">:</span>
            <span class="n">clsname</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;</span><span class="si">%s%x</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="nb">hash</span><span class="p">(</span><span class="n">key</span><span class="p">)))</span> \
                    <span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s">&#39;-&#39;</span><span class="p">,</span> <span class="s">&#39;_&#39;</span><span class="p">)</span>
            <span class="n">cls</span><span class="o">.</span><span class="n">_classcache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">clsname</span><span class="p">,</span> <span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="p">),</span> <span class="n">attrs</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">cls</span><span class="o">.</span><span class="n">_classcache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
</pre>
</div>
<p>Przykład użycia:</p>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">CategoryFactory</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">,</span> <span class="n">AbstractMixin</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>

    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
        <span class="n">abstract</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="k">def</span> <span class="nf">__unicode__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>

<span class="k">class</span> <span class="nc">ProductFactory</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">,</span> <span class="n">AbstractMixin</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>

    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
        <span class="n">abstract</span> <span class="o">=</span> <span class="bp">True</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">contribute</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">category</span><span class="p">):</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">&#39;category&#39;</span><span class="p">:</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">category</span><span class="p">)}</span>

    <span class="k">def</span> <span class="nf">__unicode__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">u&#39;</span><span class="si">%s</span><span class="s"> / </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">category</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
</pre>
</div>
<p>Konkretyzacja klas:</p>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">MyCategory</span><span class="p">(</span><span class="n">CategoryFactory</span><span class="o">.</span><span class="n">construct</span><span class="p">()):</span>
    <span class="k">pass</span>

<span class="k">class</span> <span class="nc">MyProduct</span><span class="p">(</span><span class="n">ProductFactory</span><span class="o">.</span><span class="n">construct</span><span class="p">(</span><span class="n">category</span><span class="o">=</span><span class="n">MyCategory</span><span class="p">)):</span>
    <span class="k">pass</span>
</pre>
</div>
<p>Działa również nasz test:</p>
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">models</span>

<span class="n">c</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">MyCategory</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">&#39;cat&#39;</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">MyProduct</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">&#39;prod&#39;</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="n">c</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</span>
<span class="c"># cat / prod</span>
</pre>
</div>
<p><small><a href="http://room-303.com/blog/2010/04/27/django-abstrakcji-ciag-dalszy/">Django: abstrakcji ciąg dalszy</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/04/27/django-abstrakcji-ciag-dalszy/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Django: zabawa z abstrakcją</title>
		<link>http://room-303.com/blog/2010/04/22/django-zabawa-z-abstrakcja/</link>
		<comments>http://room-303.com/blog/2010/04/22/django-zabawa-z-abstrakcja/#comments</comments>
		<pubDate>Thu, 22 Apr 2010 20:22:45 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=879</guid>
		<description><![CDATA[Zastanawialiśmy się ostatnimi czasy, w jaki sposób zbudować aplikację tak, by z jednej strony była uniwersalna (abstrakcyjne modele bazowe), a z drugiej zawierała całą niezbędną logikę. Problem polega na tym, że — z oczywistych względów — klucza obcego do modelu abstrakcyjnego stworzyć się nie da. Pozostaje więc zwalić pracę na programistę, który konkretyzuje abstrakcyjne prototypy, [...]]]></description>
			<content:encoded><![CDATA[<p>Zastanawialiśmy się ostatnimi czasy, w jaki sposób zbudować aplikację tak, by z jednej strony była uniwersalna (abstrakcyjne modele bazowe), a z drugiej zawierała całą niezbędną logikę. Problem polega na tym, że — z oczywistych względów — klucza obcego do modelu abstrakcyjnego stworzyć się nie da. Pozostaje więc zwalić pracę na programistę, który konkretyzuje abstrakcyjne prototypy, prawda?</p>
<p>Dzisiaj do głowy przyszło mi rozwiązanie może nie do końca ładne, ale na pewno działające. Fabryka abstrakcyjnych modeli:</p>
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>

<span class="k">class</span> <span class="nc">AbstractModelMetaclass</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__new__</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span>
        <span class="n">super_new</span> <span class="o">=</span> <span class="nb">super</span><span class="p">(</span><span class="n">AbstractModelMetaclass</span><span class="p">,</span> <span class="n">cls</span><span class="p">)</span><span class="o">.</span><span class="n">__new__</span>
        <span class="k">if</span> <span class="n">bases</span> <span class="o">==</span> <span class="p">(</span><span class="nb">object</span><span class="p">,</span> <span class="p">):</span>
            <span class="k">return</span> <span class="n">super_new</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">)</span>
        <span class="n">new_attrs</span> <span class="o">=</span> <span class="n">attrs</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
        <span class="n">module</span> <span class="o">=</span> <span class="n">attrs</span><span class="p">[</span><span class="s">&#39;__module__&#39;</span><span class="p">]</span>
        <span class="n">new_cls</span> <span class="o">=</span> <span class="n">super_new</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="p">{</span><span class="s">&#39;__module__&#39;</span><span class="p">:</span> <span class="n">module</span><span class="p">})</span>
        <span class="n">fields</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">new_cls</span><span class="p">,</span> <span class="s">&#39;_fields&#39;</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
        <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">attrs</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
            <span class="k">if</span> <span class="n">key</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="s">&#39;construct&#39;</span><span class="p">,</span> <span class="s">&#39;__new__&#39;</span><span class="p">]:</span>
                <span class="n">fields</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>
        <span class="n">new_attrs</span><span class="p">[</span><span class="s">&#39;_fields&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">fields</span>
        <span class="k">return</span> <span class="n">super_new</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">new_attrs</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">AbstractModelFactory</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    <span class="n">__metaclass__</span> <span class="o">=</span> <span class="n">AbstractModelMetaclass</span>

    <span class="nd">@staticmethod</span>
    <span class="k">def</span> <span class="nf">construct</span><span class="p">():</span>
        <span class="k">return</span> <span class="p">{}</span>

    <span class="k">def</span> <span class="nf">__new__</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">attrs</span> <span class="o">=</span> <span class="n">cls</span><span class="o">.</span><span class="n">_fields</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
        <span class="n">attrs</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">construct</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">))</span>
        <span class="n">attrs</span><span class="p">[</span><span class="s">&#39;__module__&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">cls</span><span class="o">.</span><span class="n">__module__</span>
        <span class="n">attrs</span><span class="p">[</span><span class="s">&#39;Meta&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s">&#39;Meta&#39;</span><span class="p">,</span> <span class="p">(),</span> <span class="p">{</span><span class="s">&#39;abstract&#39;</span><span class="p">:</span> <span class="bp">True</span><span class="p">})</span>
        <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">,</span> <span class="p">),</span> <span class="n">attrs</span><span class="p">)</span>
</pre>
</div>
<p>Idea jest taka, że implementując swoją podklasę fabryki modeli mamy możliwość zadecydować o polach, jakie trafią do ostatecznego modelu. Dla lepszego zrozumienia, rozważmy abstrakcyjne klasy produktu i kategorii:</p>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">CategoryFactory</span><span class="p">(</span><span class="n">AbstractModelFactory</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">__unicode__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span>

<span class="k">class</span> <span class="nc">ProductFactory</span><span class="p">(</span><span class="n">AbstractModelFactory</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>

    <span class="nd">@staticmethod</span>
    <span class="k">def</span> <span class="nf">construct</span><span class="p">(</span><span class="n">category</span><span class="p">):</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">&#39;category&#39;</span><span class="p">:</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">category</span><span class="p">)}</span>

    <span class="k">def</span> <span class="nf">__unicode__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">u&#39;</span><span class="si">%s</span><span class="s"> / </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">category</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
</pre>
</div>
<p>Skonkretyzujmy klasy własną implementacją:</p>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">MyCategory</span><span class="p">(</span><span class="n">CategoryFactory</span><span class="p">()):</span>
    <span class="k">pass</span>

<span class="k">class</span> <span class="nc">MyProduct</span><span class="p">(</span><span class="n">ProductFactory</span><span class="p">(</span><span class="n">category</span><span class="o">=</span><span class="n">MyCategory</span><span class="p">)):</span>
    <span class="k">pass</span>
</pre>
</div>
<p>Upewnijmy się, że zależności działają prawidłowo:</p>
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">models</span>

<span class="n">c</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">MyCategory</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">&#39;cat&#39;</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">MyProduct</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">&#39;prod&#39;</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="n">c</span><span class="p">)</span>
<span class="k">print</span> <span class="n">p</span>
<span class="c"># cat / prod</span>
</pre>
</div>
<p>Oczywiście — ktoś zaraz powie — to samo można uzyskać tworząc funkcję zwracającą klasę zdefiniowaną w jej ciele. Można, ale nie da się po niej wygodnie dziedziczyć (rozszerzenie istniejącej fabryki).</p>
<p><small><a href="http://room-303.com/blog/2010/04/22/django-zabawa-z-abstrakcja/">Django: zabawa z abstrakcją</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/04/22/django-zabawa-z-abstrakcja/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>twojastara.py</title>
		<link>http://room-303.com/blog/2010/04/14/twojastara-py/</link>
		<comments>http://room-303.com/blog/2010/04/14/twojastara-py/#comments</comments>
		<pubDate>Wed, 14 Apr 2010 13:28:27 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[varia]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=856</guid>
		<description><![CDATA[fat = unicode print type(ur&#39;mom&#39;) is fat Tak, tak, poniżej krytyki. Za to już niedługo: seria niewyjaśnionych znalezisk w kodzie Satchmo (czyli jak Pythona używać nie należy). twojastara.py © Patryk Zawadzki, Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland.]]></description>
			<content:encoded><![CDATA[<div class="highlight">
<pre><span class="n">fat</span> <span class="o">=</span> <span class="nb">unicode</span>
<span class="k">print</span> <span class="nb">type</span><span class="p">(</span><span class="s">ur&#39;mom&#39;</span><span class="p">)</span> <span class="ow">is</span> <span class="n">fat</span>
</pre>
</div>
<p>Tak, tak, poniżej krytyki. Za to już niedługo: seria niewyjaśnionych znalezisk w kodzie <a href="http://www.satchmoproject.com/">Satchmo</a> (czyli jak Pythona używać nie należy).</p>
<p><small><a href="http://room-303.com/blog/2010/04/14/twojastara-py/">twojastara.py</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/04/14/twojastara-py/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django: TestCase i testowanie szablonów</title>
		<link>http://room-303.com/blog/2010/03/15/django-testcase-i-testowanie-szablonow/</link>
		<comments>http://room-303.com/blog/2010/03/15/django-testcase-i-testowanie-szablonow/#comments</comments>
		<pubDate>Mon, 15 Mar 2010 17:03:55 +0000</pubDate>
		<dc:creator>Patrys</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://room-303.com/blog/?p=851</guid>
		<description><![CDATA[Bardzo brzydki hack, oparty o załącznik do ticketu 7611. Czyści wewnętrzny cache Django, ale pozwala testować działanie kodu opierającego się o renderowanie szablonów bez ryzyka, że aplikacja owe szablony nadpisze: import os import sys from django.conf import settings from django.template import loader from django.test import TestCase class TemplateTestCase(TestCase): def _pre_setup(self): self._template_setup() super(TemplateTestCase, self)._pre_setup() def _post_teardown(self): [...]]]></description>
			<content:encoded><![CDATA[<p>Bardzo brzydki hack, oparty o <a href="http://code.djangoproject.com/attachment/ticket/7611/7611.diff">załącznik do ticketu 7611</a>. Czyści wewnętrzny cache Django, ale pozwala testować działanie kodu opierającego się o renderowanie szablonów bez ryzyka, że aplikacja owe szablony nadpisze:</p>
<div class="highlight">
<pre><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
<span class="kn">from</span> <span class="nn">django.template</span> <span class="kn">import</span> <span class="n">loader</span>
<span class="kn">from</span> <span class="nn">django.test</span> <span class="kn">import</span> <span class="n">TestCase</span>

<span class="k">class</span> <span class="nc">TemplateTestCase</span><span class="p">(</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">_pre_setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_template_setup</span><span class="p">()</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">TemplateTestCase</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">_pre_setup</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">_post_teardown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_template_teardown</span><span class="p">()</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">TemplateTestCase</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">_post_teardown</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">_template_setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s">&#39;template_loaders&#39;</span><span class="p">):</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">_old_template_loaders</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">TEMPLATE_LOADERS</span>
            <span class="n">settings</span><span class="o">.</span><span class="n">TEMPLATE_LOADERS</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">template_loaders</span>
            <span class="n">loader</span><span class="o">.</span><span class="n">template_source_loaders</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s">&#39;template_dirs&#39;</span><span class="p">):</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">_old_template_dirs</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">TEMPLATE_DIRS</span>
            <span class="n">test_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">__module__</span><span class="p">]</span><span class="o">.</span><span class="n">__file__</span><span class="p">)</span>
            <span class="n">settings</span><span class="o">.</span><span class="n">TEMPLATE_DIRS</span> <span class="o">=</span> <span class="p">[</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">test_dir</span><span class="p">,</span> <span class="n">dirname</span><span class="p">)</span> <span class="k">for</span> <span class="n">dirname</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">template_dirs</span> <span class="p">]</span>

    <span class="k">def</span> <span class="nf">_template_teardown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s">&#39;_old_template_loaders&#39;</span><span class="p">):</span>
            <span class="n">settings</span><span class="o">.</span><span class="n">TEMPLATE_LOADERS</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_old_template_loaders</span>
            <span class="n">loader</span><span class="o">.</span><span class="n">template_source_loaders</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s">&#39;_old_template_dirs&#39;</span><span class="p">):</span>
            <span class="n">settings</span><span class="o">.</span><span class="n">TEMPLATE_DIRS</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_old_template_dirs</span>
</pre>
</div>
<p>Użycie:</p>
<div class="highlight">
<pre><span class="k">class</span> <span class="nc">TestSomething</span><span class="p">(</span><span class="n">TemplateTestCase</span><span class="p">):</span>
    <span class="n">template_loaders</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;django.template.loaders.app_directories.load_template_source&#39;</span><span class="p">,)</span>

    <span class="k">def</span> <span class="nf">test_foo</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">pass</span>
</pre>
</div>
<p><small><a href="http://room-303.com/blog/2010/03/15/django-testcase-i-testowanie-szablonow/">Django: TestCase i testowanie szablonów</a> © <a href="http://room-303.com/blog/" rel="cc:attributionURL">Patryk Zawadzki</a>, <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.5/pl/">Creative Commons Attribution-NonCommercial-ShareAlike 2.5 Poland</a>.</small></p>]]></content:encoded>
			<wfw:commentRss>http://room-303.com/blog/2010/03/15/django-testcase-i-testowanie-szablonow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
