<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Django on Noetic Nought</title>
    <link>https://punchagan.muse-amuse.in/tags/django/</link>
    <description>Recent content in Django on Noetic Nought</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <copyright>© 2006-2026 CC-BY-SA-4.0</copyright>
    <lastBuildDate>Wed, 08 Jan 2020 09:45:00 +0530</lastBuildDate>
    <atom:link href="https://punchagan.muse-amuse.in/tags/django/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Django ModelFormSet and multiple saves</title>
      <link>https://punchagan.muse-amuse.in/blog/django-modelformset-multiple-saves/</link>
      <pubDate>Wed, 08 Jan 2020 09:45:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/django-modelformset-multiple-saves/</guid>
      <description>&lt;p&gt;A friend and I were trying to &lt;a href=&#34;https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#using-a-model-formset-in-a-view&#34;&gt;use the Django ModelFormSets&lt;/a&gt; in a project, and we&#xA;ran into a subtle bug that took a little big of digging into the Django code to&#xA;identify and work around.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-bug&#34;&gt;The Bug&lt;/h2&gt;&#xA;&lt;p&gt;We weren&amp;rsquo;t really familiar with Django&amp;rsquo;s &lt;code&gt;FormSet&lt;/code&gt; or &lt;code&gt;ModelFormSet&lt;/code&gt;, when we&#xA;started. We began by reading the documentation to get started.&lt;/p&gt;&#xA;&lt;p&gt;The Django docs had &lt;a href=&#34;https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#using-a-model-formset-in-a-view&#34;&gt;an example&lt;/a&gt; that looked straight-forward, and showed how to&#xA;use &lt;code&gt;ModelFormSet&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;manage_authors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modelformset_factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Author&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fields&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;POST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FILES&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_valid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;c1&#34;&gt;# do something.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;manage_authors.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;formset&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;You create a &lt;code&gt;ModelFormSet&lt;/code&gt; class using the &lt;code&gt;modelformset_factory&lt;/code&gt; using the&#xA;required model, and the required fields.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Use the data from a &lt;code&gt;POST&lt;/code&gt; to create an instance of the class from above.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Save the data if the formset validates, and we are done!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;We tweaked the example to our needs, and it worked for us. Mostly.&lt;/p&gt;&#xA;&lt;p&gt;A user reported that after saving the form once with some data, trying to save&#xA;more data didn&amp;rsquo;t work! The form would try to save the data already saved in the&#xA;last save, and fail validation that required some of the fields to be unique.&lt;/p&gt;&#xA;&lt;p&gt;On the second save, the data which was saved during the last save was still&#xA;being treated as newly filled in data!&lt;/p&gt;&#xA;&lt;p&gt;This also caused newly filled-in data during the second save to be lost forever.&#xA;The user was forced to reload the page and re-enter the data.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-why&#34;&gt;The Why&lt;/h2&gt;&#xA;&lt;p&gt;The way Django keeps track of newly filled-in data in the form along with&#xA;multiple levels of caching causes this bug to manifest.&lt;/p&gt;&#xA;&lt;h3 id=&#34;modelformset-queryset-argument&#34;&gt;&lt;code&gt;ModelFormSet&lt;/code&gt; &lt;code&gt;queryset&lt;/code&gt; argument&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;AuthorFormSet&lt;/code&gt; (or any &lt;code&gt;ModelFormSet&lt;/code&gt; created using the &lt;code&gt;modelformset_factory&lt;/code&gt;)&#xA;takes an additional optional argument &lt;code&gt;queryset&lt;/code&gt;. This argument can be used to&#xA;choose specific rows in the DB that need to be used to populate the initial form&#xA;rows, if any. If no &lt;code&gt;queryset&lt;/code&gt; is specified, all the objects in the DB are used.&lt;/p&gt;&#xA;&lt;h3 id=&#34;formset-metadata&#34;&gt;&lt;code&gt;FormSet&lt;/code&gt;  metadata&lt;/h3&gt;&#xA;&lt;p&gt;First, metadata about the form is saved in a few hidden input fields (called the&#xA;&lt;code&gt;ManagementForm&lt;/code&gt;). This hidden form keep tracks of metadata like the total&#xA;number of forms to show, initial form count and the minimum &amp;amp; maximum number of&#xA;forms to display.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;form-TOTAL_FORMS&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;19&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;id_form-TOTAL_FORMS&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;form-INITIAL_FORMS&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;7&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;id_form-INITIAL_FORMS&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;form-MIN_NUM_FORMS&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;id_form-MIN_NUM_FORMS&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;form-MAX_NUM_FORMS&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1000&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;id_form-MAX_NUM_FORMS&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Initial form count is the count of the number of forms that are pre-filled using&#xA;data from the DB &amp;ndash; essentially, the length of the &lt;code&gt;queryset&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;metadata-does-not-change&#34;&gt;Metadata does not change&lt;/h3&gt;&#xA;&lt;p&gt;When the &lt;code&gt;ModelFormSet&lt;/code&gt; is used as described in the documentation, the metadata&#xA;in the &lt;code&gt;ManagementForm&lt;/code&gt; doesn&amp;rsquo;t get updated when the form is re-rendered in the&#xA;response to a successful &lt;code&gt;POST&lt;/code&gt; request. If one form with new data was&#xA;successfully saved, the number of initial forms in the re-rendered form must go&#xA;up by 1, but it doesn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;h3 id=&#34;queryset-caching-problem&#34;&gt;Queryset caching problem&lt;/h3&gt;&#xA;&lt;p&gt;Django has caching at the queryset level which means that the queryset doesn&amp;rsquo;t&#xA;get updated with the newly created instances, even if they match the query that&#xA;the queryset uses.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;POST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FILES&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;queryset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;existing_authors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;existing_authors&lt;/code&gt; is a queryset that looks for a specific set of authors in the&#xA;DB, or may be all the authors in the DB, if that&amp;rsquo;s what the form needs to do.&lt;/p&gt;&#xA;&lt;p&gt;So, when a formset has been submitted, and some new rows have been created in&#xA;the model&amp;rsquo;s table, the new rows don&amp;rsquo;t automatically get added to the &lt;code&gt;queryset&lt;/code&gt;&#xA;since it the results fetched from the DB are cached by the &lt;code&gt;queryset&lt;/code&gt;. We need&#xA;to create a new &lt;code&gt;queryset&lt;/code&gt; for it to include the newly created rows.&lt;/p&gt;&#xA;&lt;p&gt;These two levels of caching combined make the bug manifest, as it does. I&amp;rsquo;m not&#xA;yet sure what the correct &amp;ldquo;fix&amp;rdquo; for this is, but we ended up using a work-around&#xA;&amp;ndash; create a new formset when returning from a successful &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-work-around&#34;&gt;The Work-Around&lt;/h2&gt;&#xA;&lt;p&gt;We instantiate a new &lt;code&gt;ModelFormSet&lt;/code&gt; instance when returning the form, on a&#xA;successful &lt;code&gt;POST&lt;/code&gt;. This means, the &lt;code&gt;ManagementForm&lt;/code&gt; is recreated for the new&#xA;formset and has the correct metadata about the form, even when being rendered in&#xA;the response of a successful &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The code in the example in Django docs, would look something like the one below:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;manage_authors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modelformset_factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Author&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fields&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;POST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FILES&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_valid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;manage_authors.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;formset&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AuthorFormSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;manage_authors.html&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;formset&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;formset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This blog post should probably become a PR or an issue, but I&amp;rsquo;m not familiar&#xA;with how contributing to Django works. This blog post is an attempt to&#xA;understand the issue better, and potentially help others who get bit by it.&lt;/p&gt;&#xA;&lt;div style=&#34;font-size:small;&#34; class=&#34;reviewers&#34;&gt;&#xA;  &lt;div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Thanks to &lt;a href=&#34;http://baali.muse-amuse.in&#34;&gt;Shantanu&lt;/a&gt; for reading drafts of this post.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;</description>
    </item>
    <item>
      <title>Isolating flaky Django tests</title>
      <link>https://punchagan.muse-amuse.in/blog/isolating-flaky-django-tests/</link>
      <pubDate>Wed, 05 Jun 2019 18:37:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/isolating-flaky-django-tests/</guid>
      <description>&lt;p&gt;The Zulip repository had a bunch of flaky tests that were fixed a few weeks ago.&#xA;I learnt a couple of tricks that I wasn&amp;rsquo;t aware of during this.&lt;/p&gt;&#xA;&lt;p&gt;Django&amp;rsquo;s test runner has an &lt;a href=&#34;https://docs.djangoproject.com/en/1.11/ref/django-admin/#cmdoption-test-reverse&#34;&gt;option&lt;/a&gt; to run tests in the reverse order. The&#xA;documentation of this option explicitly mentions that this option is useful for&#xA;debugging side-effects of tests that are not properly isolated. I wasn&amp;rsquo;t aware&#xA;of this option, and it&amp;rsquo;s a pretty neat tool.&lt;/p&gt;&#xA;&lt;p&gt;The test runner also has a &lt;a href=&#34;https://docs.djangoproject.com/en/1.11/ref/django-admin/#cmdoption-test-parallel&#34;&gt;&lt;code&gt;--parallel&lt;/code&gt; option&lt;/a&gt; to run tests in parallel, and the&#xA;Zulip server uses this option when running the back-end tests. I never really&#xA;bothered to look into how test cases are assigned to different processes. The&#xA;Django docs explain:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Django distributes test cases — &lt;code&gt;unittest.TestCase&lt;/code&gt; subclasses — to subprocesses.&#xA;If there are fewer test cases than configured processes, Django will reduce the&#xA;number of processes accordingly.&lt;/p&gt;&#xA;&lt;p&gt;Each process gets its own database. You must ensure that different test cases&#xA;don’t access the same resources. For instance, test cases that touch the&#xA;filesystem should create a temporary directory for their own use.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;When running tests in parallel, the set of tests running in the same process can&#xA;vary quite a bit based on the execution speed of tests in different processes.&#xA;To add more variation to the order in that tests are run, the number of&#xA;processes being used could also be changed.&lt;/p&gt;&#xA;&lt;p&gt;These two tools combined together can get tests to run in different orders than&#xA;the usual order in which they usually get run, and can help find tests which are&#xA;not well isolated.&lt;/p&gt;&#xA;&lt;p&gt;It could be a good idea to run tests in parallel (with a randomly chosen,&#xA;reasonable number of processes) and with the &lt;code&gt;--reverse&lt;/code&gt; flag, once in every few&#xA;CI runs, or as a cron job periodically.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;div style=&#34;font-size:small;&#34; class=&#34;reviewers&#34;&gt;&#xA;  &lt;div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Thanks to &lt;a href=&#34;https://baali.muse-amuse.in&#34;&gt;Shantanu Choudhary&lt;/a&gt; for nudging me to write this post, and reading&#xA;drafts of this post. Thanks also to &lt;a href=&#34;https://blog.zulip.org/author/tabbott/&#34;&gt;Tim Abbott&lt;/a&gt; for making me aware of these&#xA;tricks.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;</description>
    </item>
    <item>
      <title>Partial postgres db dumps for a Django app</title>
      <link>https://punchagan.muse-amuse.in/blog/partial-postgres-db-dumps-for-a-django-app/</link>
      <pubDate>Sun, 10 Jan 2016 00:00:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/partial-postgres-db-dumps-for-a-django-app/</guid>
      <description>&lt;p&gt;Off late, I have been working with a large &lt;code&gt;postgres&lt;/code&gt; database that is used by&#xA;an app built in &lt;code&gt;Django&lt;/code&gt;.  I wanted a partial dump of the database to try out&#xA;some experimental clean up scripts.  I haven&amp;rsquo;t really used databases before,&#xA;and the last time I had to do this I did it in a pretty ad-hoc fashion.  This&#xA;time around, I tried to do it more methodically and to document it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-django-route&#34;&gt;The Django Route&lt;/h2&gt;&#xA;&lt;p&gt;I looked around for tools that let you do this, and found &lt;a href=&#34;https://github.com/davedash/django-fixture-magic&#34;&gt;django-fixture-magic&lt;/a&gt;.&#xA;I first tried it out on my older partial dump (10% as large as the original db)&#xA;and it turned out to be reasonably fast and worked well, after making a &lt;a href=&#34;https://github.com/davedash/django-fixture-magic/pull/35&#34;&gt;few&#xA;changes&lt;/a&gt; to get it working with Python 3.x.  Its &lt;code&gt;kitchensink&lt;/code&gt; flag to the&#xA;&lt;code&gt;dump_object&lt;/code&gt; seemed like a promising option, but &lt;strong&gt;didn&amp;rsquo;t&lt;/strong&gt; really seem to get all&#xA;the required tables for ManyToManyFields.  I worked around it, by getting a&#xA;dump of all the models which were related using Django&amp;rsquo;s &lt;code&gt;dumpdata&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;get-a-dump-with-objects-of-interest&#34;&gt;Get a dump with objects of interest&lt;/h3&gt;&#xA;&lt;p&gt;The &lt;code&gt;dump_object&lt;/code&gt; command lets you run commands to select the objects that you&#xA;want to have in the dump, and that is quite a useful thing.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python manage.py dump_object dataset.Product -k --query &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;subcategory_id__in&amp;#34;: [1886, ...]}&amp;#39;&lt;/span&gt; &amp;gt; products.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also, get a dump of related tables.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Dump of related fields&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python manage.py dumpdata dataset.Attribute &amp;gt; attributes.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;create-the-new-empty-db&#34;&gt;Create the new empty db&lt;/h3&gt;&#xA;&lt;p&gt;Next, create a new database where this fixture can be loaded!&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Create the db&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo su - postgres&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;createdb mydb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Create a user, if required&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;createuser -P&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;grant-access-to-the-user&#34;&gt;Grant access to the user&lt;/h4&gt;&#xA;&lt;p&gt;In the &lt;code&gt;psql&lt;/code&gt; prompt type the following to grant the user permissions for the&#xA;database.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;GRANT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIVILEGES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DATABASE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;myuser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;fix-settings-dot-py-and-create-tables-for-the-models-dot&#34;&gt;Fix settings.py and create tables for the models.&lt;/h3&gt;&#xA;&lt;p&gt;Make changes to &lt;code&gt;settings.py&lt;/code&gt; to use the newly created database, and then&#xA;create the tables used by the app, and then load the data.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python manage.py syncdb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python manage.py loaddata products.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;too-slow&#34;&gt;Too slow!&lt;/h3&gt;&#xA;&lt;p&gt;This method worked and was reasonably fast when I was trying to get 20k rows&#xA;from a table with about 200k rows, with all the dependencies.&lt;/p&gt;&#xA;&lt;p&gt;But, when I tried to get a dump of about 200k rows from a table with 2M rows,&#xA;it was way too slow to be of any use.  There could&amp;rsquo;ve been a couple of reasons&#xA;for this, which I didn&amp;rsquo;t have the time to look into, and debug.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The web-server where the &lt;code&gt;Django&lt;/code&gt; app was running, and the &lt;code&gt;db&lt;/code&gt; server with&#xA;the &lt;code&gt;postgres&lt;/code&gt; database were on different machines in separate datacenters,&#xA;which could&amp;rsquo;ve been adding a significant amount of latency.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Just the size of the database being much larger could be making it slower?&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These are things I should be looking into and learning about, when I have more&#xA;time at hand.  For now, I needed a quicker way to get a dump.  Even though the&#xA;raw SQL route was more manual, it turned out to be much quicker.&lt;/p&gt;&#xA;&lt;h2 id=&#34;raw-sql-dump-route&#34;&gt;Raw SQL dump route&lt;/h2&gt;&#xA;&lt;h3 id=&#34;get-a-dump-of-the-interesting-tables&#34;&gt;Get a dump of the interesting tables&lt;/h3&gt;&#xA;&lt;p&gt;First, I had to get a dump of all the tables with the data I was interested in,&#xA;one-by-one.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dataset_product&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dataset_product&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;subcategory_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;319557&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;94589&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;332&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;406&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;626&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1886&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dataset_product&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;gender_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/tmp/products.tsv&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dataset_photo&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dataset_photo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;product_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;U0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;dataset_product&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;U0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;U0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;subcategory_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;IN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;319557&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;94589&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;332&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;406&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;626&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1886&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;U0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;gender_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/tmp/photos.tsv&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- Copy a bunch of other tables!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;load-the-data-from-the-dumps&#34;&gt;Load the data from the dumps&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- syncdb&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dataset_product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/tmp/products.tsv&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ENCODING&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;UTF8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COPY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dataset_photo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/tmp/photos.tsv&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ENCODING&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;UTF8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- Copy a bunch of other tables!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;make-tables-writable&#34;&gt;Make tables writable&lt;/h3&gt;&#xA;&lt;p&gt;Some of the tables did not let me write anything to them, until I &lt;a href=&#34;http://centoshowtos.org/web-services/django-and-postgres-duplicate-key/&#34;&gt;altered the&#xA;sequence&lt;/a&gt; for these tables.&lt;/p&gt;&#xA;&lt;h3 id=&#34;automating&#34;&gt;Automating&lt;/h3&gt;&#xA;&lt;p&gt;It would be pretty nice if all of this was automated &amp;ndash; allow a user to enter&#xA;exactly the same kind of a query that &lt;code&gt;django-fixture-magic&lt;/code&gt; lets you run, and&#xA;figure out the SQL copies that need to be done to get the requested dump. Its&#xA;something that currently would qualify as yak-shaving, but may be a handy thing&#xA;to have. Someone somewhere possibly already has something that does this.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Recurse Center, 2014-08-11</title>
      <link>https://punchagan.muse-amuse.in/recurse-center/recurse-center-2014-08-11/</link>
      <pubDate>Mon, 11 Aug 2014 11:16:08 -0400</pubDate>
      <guid>https://punchagan.muse-amuse.in/recurse-center/recurse-center-2014-08-11/</guid>
      <description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I spent about an hour in the morning testing out the staging blaggregator&#xA;instance, and things seemed to work as expected.  Side note, I got added as a&#xA;collaborator on the blaggregator repo!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We worked through the first chapter on getting video working, but we mostly&#xA;ended up just copying code rather than actually understanding/carefully&#xA;studying it.  I&amp;rsquo;m not sure how I feel about it, but we got to display fancy&#xA;stuff.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I wasn&amp;rsquo;t feeling like working on any of my &amp;lsquo;old&amp;rsquo;/&amp;lsquo;ongoing&amp;rsquo; projects, and&#xA;since Mondays don&amp;rsquo;t feel very productive, I worked on writing a couple of&#xA;simple scripts to create twitter lists of HSers, batch wise.&lt;/p&gt;&#xA;&lt;p&gt;For the HS API, I just reused some old code I had lying around from my&#xA;experiments with the OAuth2 API, though I had to tweak it a little bit to be&#xA;able to actually login, using requests. Something about CSRF tokens seems to&#xA;have changed.&lt;/p&gt;&#xA;&lt;p&gt;In the process, I found that twitter&amp;rsquo;s API isn&amp;rsquo;t very pleasant to use.  Or&#xA;may be it&amp;rsquo;s the fact that I was using python twitter client without getting&#xA;my own client id/key, but the whole experience of dealing with the twitter&#xA;API wasn&amp;rsquo;t a pleasant one at all!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Amber, Daria and I also spent about an hour white-boarding some problems from&#xA;the Cracking the Code Interview book.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I ended up fixing some minor issues with blaggregator and am hoping that the&#xA;long pending PR will be merged today.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Recurse Center, 2014-07-03</title>
      <link>https://punchagan.muse-amuse.in/recurse-center/recurse-center-2014-07-03/</link>
      <pubDate>Sat, 05 Jul 2014 09:49:37 -0400</pubDate>
      <guid>https://punchagan.muse-amuse.in/recurse-center/recurse-center-2014-07-03/</guid>
      <description>&lt;ul&gt;&#xA;&lt;li&gt;I got distracted trying to add a hack to Hacker School&amp;rsquo;s &lt;a href=&#34;https://github.com/sursh/blaggregator&#34;&gt;blaggregator&lt;/a&gt;, to&#xA;enable posting to different channels on zulip.&lt;/li&gt;&#xA;&lt;li&gt;I learnt a few things about Django.  Commented out references to views that&#xA;don&amp;rsquo;t exists breaks templating.  Formsets didn&amp;rsquo;t seem very convenient to use,&#xA;for editing data showing the user only partial forms.&lt;/li&gt;&#xA;&lt;li&gt;I played around with Clang for a bit, to try and use libclang to parse the C&#xA;code for inspection, instead of writing one myself.  It sometimes feels like&#xA;an overkill, but it feels like it&amp;rsquo;ll make the whole code more robust.  I&amp;rsquo;m&#xA;not sure.  I&amp;rsquo;ll need to play around for a bit more to decide.&lt;/li&gt;&#xA;&lt;li&gt;I also finally got around to fix the resolution of my tty shells.  I was in a&#xA;bus, and wanted my laptop&amp;rsquo;s battery to last longer, and decided to use a tty&#xA;shell with just emacs running. But, the resolution sucked.  So, I&#xA;fixed. Essentially it involved removing a blacklist file that an old version&#xA;of Nvidia drivers left behind in&#xA;&lt;code&gt;/usr/share/grub-gfxpayload-lists/blacklist/&lt;/code&gt; and setting&#xA;&lt;kbd&gt;GRUB_GFXMODE=1920x1080&lt;/kbd&gt; in &lt;code&gt;/etc/default/grub&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Later in the evening I also bought the domain &lt;code&gt;octo.cat&lt;/code&gt;, given there were a&#xA;bunch of people buying cat domains, and talking about it on Zulip!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Update [7/7/14]: I forgot to write about the presentations.  There were&#xA;interesting presentations!&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/ambimorph/protagonist&#34;&gt;Protagonist&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/pirate/mesh-networking&#34;&gt;mesh networking&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://writing.brianruslim.com/2014/06/30/the-execution-context/&#34;&gt;The Execution Context&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/stijlist/toolmaker/&#34;&gt;toolmaker&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://blog.ontoillogical.com/blog/2014/07/03/doorbot-overflow/&#34;&gt;Building a better doorbot&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/buybackoff/Fredis&#34;&gt;Fredis&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
  </channel>
</rss>
