<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Markus Holtermann</title><link href="https://markusholtermann.eu/" rel="alternate"></link><link href="https://markusholtermann.eu/feeds/all.atom.xml" rel="self"></link><id>https://markusholtermann.eu/</id><updated>2021-06-06T00:00:00+02:00</updated><entry><title>Writing Safe Database Migrations in Django</title><link href="https://markusholtermann.eu/2021/06/writing-safe-database-migrations-in-django/" rel="alternate"></link><published>2021-06-06T00:00:00+02:00</published><updated>2021-06-06T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2021-06-06:/2021/06/writing-safe-database-migrations-in-django/</id><summary type="html">&lt;p class="first last"&gt;Whenever you deploy your code and apply migrations in production,
you&amp;#8217;re entering dangerous territory. I&amp;#8217;ll show common pitfalls and ways
to avoid them.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Five years ago, at DjangoCon Europe 2016 in Budapest, I gave a talk &lt;a class="reference external" href="https://markusholtermann.eu/2016/04/dont-be-afraid-of-writing-migrations/"&gt;Don&amp;#8217;t Be
Afraid Of Writing Migrations&lt;/a&gt;. Back then, while migrations in Django weren&amp;#8217;t
particularly new, they shipped in 2014, I saw many folks struggle and worry
about touching the migration files. And even fewer wrote migrations by hand.
Mind you, I wouldn&amp;#8217;t recommend writing entire migration files by hand. Django
has tools to generate them for us. But I do recommend looking at them,
understanding what they do, and then see if they need to be adjusted. Because,
in the end, as an engineer, you will always know more about your application
than Django will ever be able to.&lt;/p&gt;
&lt;p&gt;As an example, when you add a non-nullable field to a model and run
&lt;tt class="docutils literal"&gt;makemigrations&lt;/tt&gt;, Django will ask you for a one-off default value. That value
would be set for all existing rows. This is the correct behavior when there&amp;#8217;s
data in that table already. But if you know that the table is empty everywhere,
it can easily appear to be useless. As an engineer, you can make a distinction
there. As an engineer you can decide that it&amp;#8217;s fine not to have a default
value. As an engineer, you know more about your project than Django does. But
Django must be conservative to ensure the default way works for everyone.&lt;/p&gt;
&lt;h2&gt;Migrations! How? When?&lt;/h2&gt;
&lt;p&gt;I want to start this post with some general considerations. Questions like how
and when do you apply migrations? How are migrations related to deployments?
What are dos and don&amp;#8217;ts that follow from that?&lt;/p&gt;
&lt;p&gt;If you run a somewhat serious site, you are aiming for something called
zero-downtime deployments. On common way for that is something called a rolling
upgrade or a staged rollout. On a high level, it works like this.&lt;/p&gt;
&lt;p&gt;In the beginning, all your servers run the old version of you application. They
are indicated by the blue circle. Then you start rolling out the new version to
a few servers (indicated by the orange part). You keep this state for a short
while, to ensure everything keeps working. Over time, you deploy the new
version to more and more servers. Until the new version is running everywhere.&lt;/p&gt;
&lt;img alt="Stages of a staged-rollout process." class="responsive-img" src="/images/djangoconeu2021/deployment-stages.png" /&gt;
&lt;p&gt;The clear benefit of this rolling upgrade or staged rollout is the ability to
notice issues early on, way before the new version is running everywhere. The
benefit is in addition to the fact that your site remains fully available
during the entire time.&lt;/p&gt;
&lt;p&gt;But these benefits come at a price.&lt;/p&gt;
&lt;p&gt;As you can easily imaging, running two versions of you application means, they
need to be compatible. Essentially, whatever the new version does, the previous
version needs to be able to keep functioning the way it did before. This is not
always easy. And, unless this becomes a habit, this requirement can easily be
forgotten. At least until the next deployment fails because of that.&lt;/p&gt;
&lt;p&gt;But while it&amp;#8217;s often far from trivial to ensure that the backwards
compatibility is kept for the business logic, it becomes even more tricky when
we involve databases. Whatever you do with objects in the database, you need to
remember that there can always be this one server that has not been updated yet
and thus runs the previous version.&lt;/p&gt;
&lt;p&gt;Which brings me back to one of the opening questions.&lt;/p&gt;
&lt;h3&gt;When Do We Deploy Migrations?&lt;/h3&gt;
&lt;p&gt;Since you never really know when the last server was updated, but it&amp;#8217;s somewhat
easy to figure out when you start a deployment, I&amp;#8217;d recommend to always apply
migrations right before you start deploying your code.&lt;/p&gt;
&lt;p&gt;Now, this comes with some serious implications. If you do so, you cannot remove
a model or a field from a model in the same release as the one that contains
the migration. If you were to do that, because migrations run first, you&amp;#8217;d
remove a database table or a column from a table while some servers may still
try to use it.&lt;/p&gt;
&lt;p&gt;If you want to remove something, you will need to make two releases. The first
one removes the usage of the model or field from all your code. The release is
deployed everywhere, the second release can remove the table or field from the
database. This is now safe since there&amp;#8217;s no code anymore that may access them.&lt;/p&gt;
&lt;p&gt;In other words: when you add something to a model, or loosen the constraints,
such as the maximum length of a char field, you must do that before the
deployment. When you tighten the constraints or remove something, you must have
two releases, the first one that works with the new constraints and the second
one that then puts them in the database or removes a field or table.&lt;/p&gt;
&lt;p&gt;Which leaves the question: how do rename a field? Short answer, you don&amp;#8217;t. The
long answer is you add a new field, copy the data, and remove the old one.
Something similar to the third migration recipe from my previous talk.&lt;/p&gt;
&lt;p&gt;Which brings me to the next question.&lt;/p&gt;
&lt;h3&gt;How Do We Deploy Migrations?&lt;/h3&gt;
&lt;p&gt;While I&amp;#8217;d love to present the perfect solution to you, I can&amp;#8217;t. And I can&amp;#8217;t,
for several reasons.&lt;/p&gt;
&lt;p&gt;For once, everyone, every team, every company has their own processes for
releasing new versions of their software.&lt;/p&gt;
&lt;p&gt;The spectrum of how these processes work is shier endless. There&amp;#8217;s that super
hip startup, that has a &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline that automatically deploys to a staging
system where integration and end-to-end tests are run. And if all of them pass,
the &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; automatically deploys to production. And the release pipeline runs
for each merged pull request.&lt;/p&gt;
&lt;p&gt;On the other hand, you have enterprises where a database administrator types in
the &lt;span class="caps"&gt;SQL&lt;/span&gt; statement manually to make a change to the database. And that process
is preceded with a change request process where 3 department heads have to sign
off. Or something like that.&lt;/p&gt;
&lt;p&gt;If we look a little bit below the surface of the variety of those processes, we
can see that they all have something in common.&lt;/p&gt;
&lt;h3&gt;Only Go Forwards &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Never Look Back&lt;/h3&gt;
&lt;p&gt;They all only make changes in one direction: forwards.&lt;/p&gt;
&lt;p&gt;If you think about it, it actually makes sense. If you made a change to the
database, you might not be able to undo it. Data that was removed is gone. You
can&amp;#8217;t magically undo a &lt;span class="caps"&gt;DROP&lt;/span&gt; &lt;span class="caps"&gt;TABLE&lt;/span&gt; statement once a transaction is committed.
What you can do, however, is recreate that table and restore the data from a
backup that you obviously took before applying the migration and that you
obviously also tested. Because that&amp;#8217;s what one does, isn&amp;#8217;t it?&lt;/p&gt;
&lt;p&gt;The thing is, the moment something is shared with others, and that&amp;#8217;s kind of
the point of databases, you have to expect that somebody else is using it.&lt;/p&gt;
&lt;p&gt;So, why does Django provide a way to unapply a migration? Well, I don&amp;#8217;t know.
But I do know that it&amp;#8217;s quite a useful tool during development. But when it
comes to databases for staging and production and such, I&amp;#8217;d really recommend to
not go backwards. Depending on when exactly the migration occurs compared to
when new code is deployed, you may even be running code that expected a
migration to be applied.&lt;/p&gt;
&lt;p&gt;And, given two migrations that depend on each other, but where the first one
has additional, unrelated changes to the second one, how would you roll back
those unrelated changes? The answer to that is, a new migration that rolls back
the corresponding changes.&lt;/p&gt;
&lt;p&gt;This &amp;#8220;only go forward&amp;#8221; and &amp;#8220;apply migrations before deployment&amp;#8221; has gone so far
for the Django projects I maintain, that the entrypoint script for my Docker
containers is this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; -e

&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;until&lt;/span&gt; django-admin dbshell -- --command &lt;span class="s1"&gt;&amp;#39;\q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Postgres is unavailable - sleeping&amp;quot;&lt;/span&gt;
  sleep &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Postgres is up - executing command&amp;quot;&lt;/span&gt;

django-admin migrate -v &lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nv"&gt;$cmd&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#8217;ll first try to connect to the database, PostgreSQL in this case, until it
succeeds. Once done, I apply all migrations in the project. And then execute
the actual command, such as running gunicorn.&lt;/p&gt;
&lt;p&gt;This approach works very, very well for me.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a small gotcha, though. Since applying the migrations is part of the
entrypoint of each Docker container, Django will attempt to apply migrations
each time a container starts, which adds to the startup time. However, if no
migrations need to be applied, the migrate command is like a no-op. However,
when you think back about the staged rollout, you must make sure that the very
first stage is exactly one Docker container.&lt;/p&gt;
&lt;p&gt;Now, after all this theory, let&amp;#8217;s look at something more hands-on.&lt;/p&gt;
&lt;h2&gt;Adding A Field Is Harmless&lt;/h2&gt;
&lt;p&gt;Our database models evolve over time. And one of the most frequent changes we
do to our models, is adding field. And doing so seems rather harmless, doesn&amp;#8217;t
it?&lt;/p&gt;
&lt;p&gt;We have two models.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddFieldModel1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddFieldModel2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the first one, we add a nullable field, in the second one, we add a field
with an explicit default value. This seems fine, right?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddFieldModel1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddFieldModel2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aaaaaaaaaa&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First, let&amp;#8217;s look at the migration that Django creates&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;add_field&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0001_initial&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;addfieldmodel1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;field&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;addfieldmodel2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;field&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aaaaaaaaaa&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For those of you who have looked at migration files before, this is nothing
new. For everyone else, let me briefly explain what you can see here:&lt;/p&gt;
&lt;p&gt;First, this migration depends on another one, namely migration &lt;tt class="docutils literal"&gt;0001_initial&lt;/tt&gt;
form the app &lt;tt class="docutils literal"&gt;add_field&lt;/tt&gt;. Which means, this migration can only ever be
applied to the database, when that dependency has been applied. Or in reverse:
when you are applying this migration, that dependency will be applied before.&lt;/p&gt;
&lt;p&gt;Second, you see a list of operations. An operation is Django&amp;#8217;s abstraction
around some so called database instructions that alter your database, such as
adding and removing database columns, creating and removing database tables,
and more.&lt;/p&gt;
&lt;p&gt;The two operations here, each add a field called &lt;tt class="docutils literal"&gt;field&lt;/tt&gt; to the models
&lt;tt class="docutils literal"&gt;addfieldmodel1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;addfieldmodel2&lt;/tt&gt;, respectively. The field that is
added is then describe there.&lt;/p&gt;
&lt;p&gt;We can now use Django&amp;#8217;s &lt;tt class="docutils literal"&gt;sqlmigrate&lt;/tt&gt; command to get the underlying &lt;span class="caps"&gt;SQL&lt;/span&gt;
commands.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Add field field to addfieldmodel1&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_field_addfieldmodel1&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;field&amp;quot;&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Add field field to addfieldmodel2&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_field_addfieldmodel2&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;field&amp;quot;&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;aaaaaaaaaa&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_field_addfieldmodel2&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;field&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All of these commands still look fairly harmless, don&amp;#8217;t they?&lt;/p&gt;
&lt;p&gt;Well, you might have guessed it, the answer is no!&lt;/p&gt;
&lt;p&gt;The first &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;ALTER&lt;/span&gt; &lt;span class="caps"&gt;TABLE&lt;/span&gt;&lt;/tt&gt; is kind of okay, but the second one can cause you
some real headache.&lt;/p&gt;
&lt;p&gt;To understand why, we need to understand how databases handle these types of
schema alterations.&lt;/p&gt;
&lt;p&gt;Adding a nullable column, as we do in the first case, is nothing more than some
metadata update. The so called table header will include the new column, a flag
that its nullable, and that&amp;#8217;s it. None of the existing records will need to be
updated. Any new record that has a non-null value, will include that value.&lt;/p&gt;
&lt;p&gt;For our second case, however, the database will not only need to add the column
to the table header, but it will also need to go through all database records
in that table and set the default value. And this can take quite some time, if
you have a table with a lot of records.&lt;/p&gt;
&lt;p&gt;Additionally, since your database will take a fairly heavy lock on the table,
you might even render your site inaccessible, in case the table you&amp;#8217;re
modifying is used rather frequently. Because both read and write queries might
be blocked.&lt;/p&gt;
&lt;p&gt;That is, unless you use PostgreSQL 11 or newer, which also deals with the
second case in a clever and very efficient way. However, since you might not
know which database your code is running on, for example, because you&amp;#8217;re
writing a reusable Django app, it&amp;#8217;s a good idea to always take approach number
one and scratch the idea of adding a default value out of your head.&lt;/p&gt;
&lt;h2&gt;But I Want A Default Value!&lt;/h2&gt;
&lt;p&gt;Well, okay. You can get a default value. &lt;a class="reference external" href="https://markusholtermann.eu/2016/04/django-migrations-recipe-2/"&gt;The migration recipe number two&lt;/a&gt; in
talk linked before gives you step-by-step instructions.&lt;/p&gt;
&lt;p&gt;However, I&amp;#8217;d only recommend that approach for tables with a fairly small amount
of records.&lt;/p&gt;
&lt;p&gt;That is, because Django runs each migration inside a transaction. If you&amp;#8217;re
updating a hundred million records at ones, depending on what your application,
or rather its users, might be doing during that time, you can easily get to a
point where the transaction needs to be rolled back. Imaging going through 99
million records and then the transaction fails. That&amp;#8217;s more than annoying. To
ensure that doesn&amp;#8217;t happen, you&amp;#8217;d need to get a write lock on all records in
the table, which can again lead to an unavailability of your entire site.&lt;/p&gt;
&lt;p&gt;So, how do you deal with this?&lt;/p&gt;
&lt;p&gt;Write a management command and run that after applying the migration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.management.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseCommand&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;safe_migrations.add_field.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AddFieldModel1&lt;/span&gt;

&lt;span class="n"&gt;CHUNK_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;


    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseCommand&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CHUNK_SIZE&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;AddFieldModel1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field__isnull&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_for_update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="n"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AddFieldModel1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bbbbbbbbbb&amp;quot;&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The management command will lock at most 5000 objects at a time, and then
update their field value.&lt;/p&gt;
&lt;p&gt;By using &lt;tt class="docutils literal"&gt;select_for_update()&lt;/tt&gt; for each chunk, you can be sure that the field
value for those objects won&amp;#8217;t be overridden by anybody else in the meantime.&lt;/p&gt;
&lt;p&gt;Sure, running this command will take longer than updating all records at once
while locking your table. But it allows you to keep your site operational.
Which, very often, I guess, is more important.&lt;/p&gt;
&lt;p&gt;But coming back to what I said earlier, as an engineer you know more about the
project than Django does, this applies here as well. If you know that the table
you&amp;#8217;re adding a field to is small or maybe even empty, it&amp;#8217;s absolutely okay to
add a default value.&lt;/p&gt;
&lt;p&gt;Which brings me to another topic. Databases are usually pretty good a
retrieving data very efficiently. So much so, that, until a certain threshold,
a full table scan is more efficient than looking up rows in an index. But at
some point, your table outgrows that point and you need an index.&lt;/p&gt;
&lt;h2&gt;Adding An Index&lt;/h2&gt;
&lt;p&gt;Modern Django versions provide not just one but two ways to do so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddIndexModel1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddIndexModel2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Firstly, the old way that&amp;#8217;s been around forever. You can set &lt;tt class="docutils literal"&gt;db_index=True&lt;/tt&gt;
on a field and Django will create an index.&lt;/p&gt;
&lt;p&gt;Secondly, since Django 1.11, you can define class based indexes in a model&amp;#8217;s
&lt;tt class="docutils literal"&gt;Meta&lt;/tt&gt; class. They are far more flexible, and powerful. And since Django 3.2
you can even add indexes on expressions, also known as functional indexes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddIndexModel1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddIndexModel2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;my_idx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&amp;#8217;s actually a third option. The &lt;tt class="docutils literal"&gt;index_together&lt;/tt&gt; / &lt;tt class="docutils literal"&gt;unique_together&lt;/tt&gt;
attributes in the model&amp;#8217;s &lt;tt class="docutils literal"&gt;Meta&lt;/tt&gt; class allow you to create indexes on
multiple columns.  Personally, I&amp;#8217;d consider them outdated as well.
Additionally, for the example at hand, I&amp;#8217;m going to ignore them. Because they
behave identically to &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt; and can be replaced with class-based
indexes.&lt;/p&gt;
&lt;p&gt;Looking at the auto generated migration, you can see an &lt;tt class="docutils literal"&gt;AlterField&lt;/tt&gt; which
adds the &lt;tt class="docutils literal"&gt;db_index=True&lt;/tt&gt;, as well as an &lt;tt class="docutils literal"&gt;AddIndex&lt;/tt&gt; operation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;add_index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0001_initial&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;addindexmodel1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;addindexmodel2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;my_idx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A downside of the &lt;tt class="docutils literal"&gt;AlterField&lt;/tt&gt; operation is, that you don&amp;#8217;t really see on the
Python level what changed on the field. You need to search for the last
migration operation involving a field in order to be able to tell that the
index was added.&lt;/p&gt;
&lt;p&gt;In contrast to that, the &lt;tt class="docutils literal"&gt;AddIndex&lt;/tt&gt; operation is clear in what it does: it
adds an index.&lt;/p&gt;
&lt;p&gt;When we now look at the generated &lt;span class="caps"&gt;SQL&lt;/span&gt;, we can see something very interesting:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Alter field name on addindexmodel1&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1_name_adf72323&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1_name_adf72323_like&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;varchar_pattern_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Create index my_idx on field(s) name of model addindexmodel2&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;my_idx&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel2&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Firstly, &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt; not only adds a single index, but it adds two. The first
one is the one that we all expect. The second one, however, is one that Django
adds to make &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;LIKE&lt;/span&gt;&lt;/tt&gt; queries efficient.&lt;/p&gt;
&lt;p&gt;Secondly, the name for the auto-generated &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt; indexes is unpleasant to
look at. The 8 random characters are part of an &lt;span class="caps"&gt;MD5&lt;/span&gt; hash over several
attributes to uniquely identify the index.&lt;/p&gt;
&lt;p&gt;Using the class based index, we can, however define out own index name, which
makes it so much more pleasant to look at. Using meaningful index names has the
added benefit that it&amp;#8217;s easier to debug database issue. The index name can
carry additional context that then allows the database administrators to debug
certain issues more effectively. But it&amp;#8217;s important to know that some
databases, among them PostgreSQL requires an index name to be unique within a
database. Using &lt;tt class="docutils literal"&gt;my_idx&lt;/tt&gt; as I did in the example here, is probably not the
best idea. But it&amp;#8217;s short to read and makes the code fit on the slides.&lt;/p&gt;
&lt;p&gt;Now, if you go ahead and apply this migration on your database, you&amp;#8217;ll be fine
when there&amp;#8217;s not really any load on it and when a table doesn&amp;#8217;t have a lot of
records. However, as with the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;ADD&lt;/span&gt; &lt;span class="caps"&gt;COLUMN&lt;/span&gt;&lt;/tt&gt; example earlier, this operation
can lock your table for quite a while.&lt;/p&gt;
&lt;p&gt;And the worst thing, using &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt;, it does so twice. Once for each index.
Even if you&amp;#8217;ll never use the one for &lt;span class="caps"&gt;LIKE&lt;/span&gt; queries.&lt;/p&gt;
&lt;p&gt;I got to admit, though, using a &lt;tt class="docutils literal"&gt;CharField&lt;/tt&gt; as an example here is the worst
example I could give. If you set &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt; on an &lt;tt class="docutils literal"&gt;IntegerField&lt;/tt&gt; Django
will only create one index. But this demonstrates that it&amp;#8217;s a good idea to look
at the migration files and see what they&amp;#8217;ll actually do.&lt;/p&gt;
&lt;p&gt;So, how do we fix the table lock issue?&lt;/p&gt;
&lt;p&gt;Well, PostgreSQL can build indexes concurrently, while allowing access to the
data in the underlying table. That, however, comes with the downside that this
needs to run outside of transactions.&lt;/p&gt;
&lt;p&gt;Since each migration runs within a transaction, we need to set
&lt;tt class="docutils literal"&gt;atomic=False&lt;/tt&gt;.  Then we can use &lt;tt class="docutils literal"&gt;AddIndexConcurrently&lt;/tt&gt; to turn our
class-based index into one that&amp;#8217;s added concurrently.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.postgres.operations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AddIndexConcurrently&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

&lt;span class="n"&gt;atomic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;add_index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0001_initial&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;addindexmodel1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;AddIndexConcurrently&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;addindexmodel2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;my_idx&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s look at what actually changed on the &lt;span class="caps"&gt;SQL&lt;/span&gt; level:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Alter field name on addindexmodel1&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1_name_adf72323&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1_name_adf72323_like&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel1&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;varchar_pattern_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Create index my_idx on field(s) name of model addindexmodel2&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;my_idx&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;add_index_addindexmodel2&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;BEGIN&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COMMIT&lt;/span&gt;&lt;/tt&gt; statements are gone. And the last
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CREATE&lt;/span&gt; &lt;span class="caps"&gt;INDEX&lt;/span&gt;&lt;/tt&gt; statement now has an additional &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CONCURRENTLY&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Now, if you&amp;#8217;re asking yourself how you deal with that on MySQL and MariaDB, I
got to disappoint you: you don&amp;#8217;t. Because luckily, you do not even need to,
because adding indexes there happens without locking the whole table.&lt;/p&gt;
&lt;h2&gt;Test Your Migrations&lt;/h2&gt;
&lt;p&gt;Even with all these suggestions and tips, one thing remains. You should test
your migrations. I&amp;#8217;m not necessarily talking about unit tests. Yes, maybe, it
depends. No, I mean, you should test your migrations in a production-like
environment. Have some test scenarios available that you can refer to when
migrations touch a particularly large table or one that&amp;#8217;s accessed frequently.
See and try out how the database behaves.&lt;/p&gt;
&lt;p&gt;But it&amp;#8217;s important to understand, that this level of testing of migrations is
not something I&amp;#8217;d do for each migration. But it&amp;#8217;s something that can help you
understand how your database works and what impact on the production
environment you might see. But in the end, whatever you do in a testing
environment, it&amp;#8217;s not your production environment and thus _will_ behave
slightly differently. Even if it&amp;#8217;s just for the users that behave different
than usual.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Which brings me to the end of this talk.&lt;/p&gt;
&lt;p&gt;Let me briefly summarize what we&amp;#8217;ve seen today:&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s usually a good idea to apply migrations before you deploy and run new
code. While not trivial, it&amp;#8217;s relatively easy to wrap one&amp;#8217;s head around it.
&lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;AddField&lt;/tt&gt; can go into the same release as the code;
&lt;tt class="docutils literal"&gt;DeleteModel&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;RemoveField&lt;/tt&gt; need a separate release. Renaming is a
combination of add and remove.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s a good approach to only ever go forwards. Rolling back database migrations
can lead to additional unexpected behavior, in addition to the one you&amp;#8217;re
facing already.&lt;/p&gt;
&lt;p&gt;When adding fields to existing models, make it a habit to add nullable columns
without a default value. It&amp;#8217;s a good pattern that&amp;#8217;s always safe.&lt;/p&gt;
&lt;p&gt;If you want default values, that&amp;#8217;s fine, but populate existing rows manually.&lt;/p&gt;
&lt;p&gt;When you add indexes, try to do that concurrently. Again, especially on bigger
tables.&lt;/p&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category></entry><entry><title>A Pony On The Move: How Migrations Work In Django 🐎</title><link href="https://markusholtermann.eu/2021/06/a-pony-on-the-move-how-migrations-work-in-django/" rel="alternate"></link><published>2021-06-05T00:00:00+02:00</published><updated>2021-06-05T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2021-06-05:/2021/06/a-pony-on-the-move-how-migrations-work-in-django/</id><summary type="html">&lt;p class="first last"&gt;While migrations are widely used by almost every Django project ever
since, there is little known about the internals of the migration framework.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Django&amp;#8217;s migration framework has been around since version 1.7 in 2014. From
the outset, it had a long journey ahead, considering all the learnings and
experience Andrew Godwin had with the 3rd party package South. However,
Django&amp;#8217;s migrations are not to be considered a rewrite of South as they work
fundamentally different.&lt;/p&gt;
&lt;p&gt;This talk will give an insight view into the components that make up Django&amp;#8217;s
migration framework. The talk will lay-out how these components fit and work
together. It will outline major challenges and problems that were encountered
and overcome along the way.&lt;/p&gt;
&lt;p&gt;One of the goals of this talk is to reduce the entry barrier for potential
Django contributors to the rather complex migration framework. There are some
parts that are tricky and non-trivial and non-obvious. Thus, this talk is
absolutely aimed at prospective contributors, regardless if they are first-time
Django contributors or ones with a year-long track record.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;img alt="Alphabetical list of all pieces that are part of Django's migration framework." class="responsive-img" src="/images/djangoconeu2020/components-00-unordered.png" /&gt;
&lt;p&gt;These 17 blocks are the main parts that make Django&amp;#8217;s migration framework.  And
I want to try to bring some order to them. You might notice that I didn&amp;#8217;t
include the management commands. Nor did I include the individual database
operations. The management commands use some of these components, but they are
not particularly complex, compared to the rest. The individual migration
operations are grouped together as &amp;#8220;Operations&amp;#8221; in the second row to the right.&lt;/p&gt;
&lt;p&gt;I think the best way to structure this pile of boxes is to not sort them
alphabetically as it is done here.&lt;/p&gt;
&lt;p&gt;Instead I am going to arrange them with some arrows, showing which component is
using which other component. That means, &amp;#8220;A uses B&amp;#8221; will be shown by an arrow
pointing from A to B.&lt;/p&gt;
&lt;img alt="Ordered pieces such that the related ones are connected by arrows" class="responsive-img" src="/images/djangoconeu2020/components-01-all-white.png" /&gt;
&lt;p&gt;As you might notice, and also might have expected, there is a lot going on
here. And I do mean A &lt;span class="caps"&gt;LOT&lt;/span&gt;! I&amp;#8217;ve also omitted some arrows to keep it readable.&lt;/p&gt;
&lt;p&gt;This talk will only cover the core parts of the migration system; other parts
are ignored. But just because they aren&amp;#8217;t covered here it doesn&amp;#8217;t mean they&amp;#8217;re
not important.  Each of these boxes is fundamental to the overall working of
the migration framework.&lt;/p&gt;
&lt;img alt="What's covered in this post" class="responsive-img" src="/images/djangoconeu2020/components-02-cut-out.png" /&gt;
&lt;p&gt;So, here&amp;#8217;s what this post is going to cover. It&amp;#8217;ll be about the core of the
migration framework, the applying and un-applying of migrations and how Django
figures out what you changed in your models to then create new migration files.&lt;/p&gt;
&lt;p&gt;I will highlight the boxes as they&amp;#8217;re being covered, and gray out all others
that weren&amp;#8217;t covered yet. Essentially, we&amp;#8217;re going to be the pony that&amp;#8217;s
migrating through this graph of boxes.&lt;/p&gt;
&lt;h2&gt;ModelStates, ProjectStates, StateApps&lt;/h2&gt;
&lt;img alt="Highlighted the state components in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-04-states.png" /&gt;
&lt;p&gt;I want to start with these three pieces: &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s, &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;s,
and &lt;tt class="docutils literal"&gt;StateApps&lt;/tt&gt;. If you want to define a hierarchy of importance among all
components making Django&amp;#8217;s migration framework, these three are by far the most
important ones. Nothing else works without them.&lt;/p&gt;
&lt;p&gt;When we talk about migrations, we&amp;#8217;re almost always talking about a timeline of
your project. And we look at the state of the database and the corresponding
models at some point in the life of your project. A &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; is
essentially a snapshot at an arbitrary point in time on the timeline.&lt;/p&gt;
&lt;p&gt;A &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt; represents a single model at some point on the timeline. In
many ways, it looks like a Django model. But it isn&amp;#8217;t. It&amp;#8217;s a lightweight class
without a lot of magic or logic. Because this class is instantiated hundreds,
often thousands, and sometimes even millions of times. It contains a model&amp;#8217;s
name and app label. It&amp;#8217;s fields, indexes, and constraints. And stuff like that.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;StateApps&lt;/tt&gt; are pretty much an App registry as you may know from
&lt;tt class="docutils literal"&gt;django.apps.registry.Apps&lt;/tt&gt;. The place where all models of your Django
project are registered. Its purpose inside the migration framework becomes more
clear when we look at the Migration Executor later on. For now, let&amp;#8217;s say, it&amp;#8217;s
an App registry with some models at some point in time, much like the
&lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;I just said, a &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; is a snapshot of models at some &amp;#8220;point in
time&amp;#8221;.  What that means is, whenever you or Django changes something in the
models, such as deleting a field or adding a model, that is a new state of your
project. Because your models before and after these events are different.&lt;/p&gt;
&lt;p&gt;In Django&amp;#8217;s migration system terminology, these &amp;#8220;events&amp;#8221; are called migration
operations. And that&amp;#8217;s what we&amp;#8217;re looking at next.&lt;/p&gt;
&lt;h2&gt;Migrations and Operations&lt;/h2&gt;
&lt;img alt="Highlighted the migration components in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-05-migrations.png" /&gt;
&lt;p&gt;There are about two dozen migration operations. They are for example
&lt;tt class="docutils literal"&gt;AddField&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;RemoveField&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;DeleteModel&lt;/tt&gt;, but also
less commonly used ones, like &lt;tt class="docutils literal"&gt;AlterOrderWithRespectTo&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;RemoveConstraint&lt;/tt&gt;, or for example, PostgreSQL&amp;#8217;s specific one,
&lt;tt class="docutils literal"&gt;CreateExtension&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Operations have two tasks. They look at an instance of a &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; and
then mutate the &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s that are in the &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;. It&amp;#8217;s
important to know that there&amp;#8217;s only ever a &amp;#8220;mutate forward&amp;#8221;. The method on the
&lt;tt class="docutils literal"&gt;Operation&lt;/tt&gt; classes for that is called &lt;tt class="docutils literal"&gt;state_forwards()&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The second thing operations do, is deciding on what to do with the database.
For that, and unlike for the state, there are the &lt;tt class="docutils literal"&gt;database_forwards()&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;database_backwards()&lt;/tt&gt; methods. They talk to the &lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt; and ask it
for example to create a new table or add a column to an existing table.&lt;/p&gt;
&lt;p&gt;A &lt;tt class="docutils literal"&gt;Migration&lt;/tt&gt; is a bundle of these Operations. All Operations within a
migration are executed in a single database transaction. That is, if the
underlying database supports that.&lt;/p&gt;
&lt;p&gt;A single migration is also always bound to a single Django app. Which means,
all operations within the migration work on the same Django app. But, and I
can&amp;#8217;t stress that enough, each operation has access to the whole
&lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;, and thus to all apps and models. That is important when
migrations deal with &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt;s. Because, let&amp;#8217;s say, you change a model&amp;#8217;s
primary key from an Integer to a String. Then the &lt;tt class="docutils literal"&gt;AlterField&lt;/tt&gt; operation will
go through all models and check if there&amp;#8217;s a field that points to the model you
just change. And if there is, it will change that field&amp;#8217;s database column&amp;#8217;s
data type as well.&lt;/p&gt;
&lt;p&gt;Now that we know how the migration system keeps track of state and how to
mutate the state and talk to the database, how does the framework know in which
order the changes are applied?&lt;/p&gt;
&lt;h2&gt;Migration Graph&lt;/h2&gt;
&lt;img alt="Highlighted the graph component in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-06-graph.png" /&gt;
&lt;p&gt;The answer to that is the &lt;tt class="docutils literal"&gt;MigrationGraph&lt;/tt&gt;. And I&amp;#8217;ll only briefly cover this
one. In a mathematical sense, it&amp;#8217;s a so-called directed acyclic graph.&lt;/p&gt;
&lt;p&gt;A Migration has two, one might argue three, attributes that the graph considers
for ordering:&lt;/p&gt;
&lt;p&gt;First, and most commonly used: &lt;tt class="docutils literal"&gt;dependencies&lt;/tt&gt;. That&amp;#8217;s a list of other
migrations that need to be applied before the migration in question can be
applied. Let&amp;#8217;s say, you have a migration that creates a model, and another
migration that adds a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; on another model to that model. Well, The
target model has to be created first. Otherwise the foreign key constraint
can&amp;#8217;t be fulfilled.&lt;/p&gt;
&lt;p&gt;Second, barely known and less often used is the attribute &lt;tt class="docutils literal"&gt;run_before&lt;/tt&gt;. It
works exactly like &lt;tt class="docutils literal"&gt;dependencies&lt;/tt&gt;. Just the other way around.&lt;/p&gt;
&lt;p&gt;And third, there is the &lt;tt class="docutils literal"&gt;replaces&lt;/tt&gt; attribute on a migration. Which comes into
play when you squash migrations. And it essentially replaces a set of
migrations with a single other one.&lt;/p&gt;
&lt;p&gt;Now, the question arises, how does Django know about the migrations in your
project? For that, there&amp;#8217;s the migration loader&lt;/p&gt;
&lt;h2&gt;Migration Loader&lt;/h2&gt;
&lt;img alt="Highlighted the loader component in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-07-loader.png" /&gt;
&lt;p&gt;The potentially obvious task for the &lt;tt class="docutils literal"&gt;MigrationLoader&lt;/tt&gt;, is loading all
migration files from all apps in your Django project and adding them to the
graph that we just talked about. As part of that, the loader is actually going
to import the Python file that contains the migration and instantiate the
&lt;tt class="docutils literal"&gt;Migration&lt;/tt&gt; class with the &lt;tt class="docutils literal"&gt;app_label&lt;/tt&gt;. This is where the migration gets
the &lt;tt class="docutils literal"&gt;app_label&lt;/tt&gt; from, since it&amp;#8217;s not an attribute on the class.&lt;/p&gt;
&lt;p&gt;But building the graph is easier said than done. Because, remember how I just
quickly skipped over the &lt;tt class="docutils literal"&gt;replaces&lt;/tt&gt; attribute on Migrations, and how it&amp;#8217;s
used with migration squashing? Well, building the _actual_ graph in the
migration loader will need to account for that. The graph may go and replace
some migrations with a replacement. But if some of the replaced migrations have
already been applied, then the replacing one cannot be used. Instead, the
remaining replaced migrations need to be used.&lt;/p&gt;
&lt;p&gt;The loader does a few more things. For example, it checks that the migration
history is sound. If you have applied migrations but some of its dependencies
haven&amp;#8217;t been applied. It&amp;#8217;s going to yell at you.&lt;/p&gt;
&lt;p&gt;And lastly, the loader provides an interface, to create a &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; from
the underlying graph.&lt;/p&gt;
&lt;p&gt;At this point, let&amp;#8217;s recap what we have talked about so far. We know how to
load migrations. We know how to turn them into a graph that defines in which
order to do what. And we know the underlying data types that represent a
database state at some time.&lt;/p&gt;
&lt;p&gt;Which brings us to our next component.&lt;/p&gt;
&lt;h2&gt;Migration Executor&lt;/h2&gt;
&lt;img alt="Highlighted the executor component in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-08-executor.png" /&gt;
&lt;p&gt;It&amp;#8217;s the brain of applying and unapplying migrations. From the outside, there&amp;#8217;s
only one important method: &lt;tt class="docutils literal"&gt;migrate()&lt;/tt&gt;. The method takes one required
argument: &lt;tt class="docutils literal"&gt;targets&lt;/tt&gt;. Targets are the names of the migrations that you want to
have applied or unapplied at the end of the method call. Which means, if you
want to apply your whole project, it is a list of all the last migrations in
each app. These migrations are also called leafs.&lt;/p&gt;
&lt;p&gt;From those targets, the executor will attempt to build a migration plan. If
you&amp;#8217;re curious, you can use the &lt;tt class="docutils literal"&gt;showmigrations&lt;/tt&gt; management command with the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--plan&lt;/span&gt;&lt;/tt&gt; option to see what the plan would look like when you apply your
whole project.&lt;/p&gt;
&lt;p&gt;As part of building the plan, the executor will look at which migrations have
already been applied and which ones are still outstanding. Or vice versa, the
migrations that are applied and need to be unapplied.&lt;/p&gt;
&lt;p&gt;Now, when migrating forward, that is, applying migrations, the executor is
going to start with a fresh &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; and then iterates over all
migrations in the plan and calls &lt;tt class="docutils literal"&gt;mutate_state()&lt;/tt&gt; on each migration. Each
migration will then in turn call &lt;tt class="docutils literal"&gt;state_forwards()&lt;/tt&gt; on each migration
operation. That builds up the representation of your database operation by
operation. And it does that, up until the point when the first migration will
need to be applied.  Which means, up until the point when operations will need
to talk to the database through the &lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;At that point, the most crucial part of the migration process occurs: rendering
models. After the rendering, the executor is going to call the &lt;tt class="docutils literal"&gt;apply()&lt;/tt&gt;
methods of each migration. Which will mutate the state forwards operation by
operation, and also applies the database changes operation by operation.&lt;/p&gt;
&lt;p&gt;Before I go into the unapplying part I want to demystify the &amp;#8220;rendering of
models&amp;#8221;. Remember how, in the beginning I said that &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s are &amp;#8220;just
like a model&amp;#8221;? And how a &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; knows about all &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s at a
given time?&lt;/p&gt;
&lt;h3&gt;Model Field References&lt;/h3&gt;
&lt;img alt="Example code on model field references" class="responsive-img" src="/images/djangoconeu2020/model-field-references.png" /&gt;
&lt;p&gt;Model classes and their fields have references to each other and among each
other. For example, you can use a model&amp;#8217;s &lt;tt class="docutils literal"&gt;_meta&lt;/tt&gt; &lt;span class="caps"&gt;API&lt;/span&gt; and get all fields on
that model. And each field will have a &lt;tt class="docutils literal"&gt;model&lt;/tt&gt; attribute, pointing back to
that model.&lt;/p&gt;
&lt;p&gt;And if you for example add a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; on model &lt;tt class="docutils literal"&gt;A&lt;/tt&gt;, pointing to model
&lt;tt class="docutils literal"&gt;B&lt;/tt&gt;, then Django will automatically add a reverse &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt;, which is a
&lt;tt class="docutils literal"&gt;ManyToOneRel&lt;/tt&gt;, from &lt;tt class="docutils literal"&gt;B&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;A&lt;/tt&gt;. Which is effectively a field on &lt;tt class="docutils literal"&gt;B&lt;/tt&gt;.
And the fields themselves will have the attributes &lt;tt class="docutils literal"&gt;related_model&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;remote_field&lt;/tt&gt; which point to the corresponding part on the other side of the
relation. So, adding a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;A&lt;/tt&gt; not only changes &lt;tt class="docutils literal"&gt;A&lt;/tt&gt; but also
&lt;tt class="docutils literal"&gt;B&lt;/tt&gt;. And just with a few models and &lt;tt class="docutils literal"&gt;ForeignKeys&lt;/tt&gt; you will end up with
millions of pointers that the migration framework would need to keep track of.&lt;/p&gt;
&lt;h3&gt;Performance Considerations&lt;/h3&gt;
&lt;p&gt;To avoid that, the migrations work on &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s, which do not have these
references. And because the &lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt; only works with model classes, the
&lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s need to be converted into them. And that&amp;#8217;s called &amp;#8220;model
rendering&amp;#8221;. And is a far better approach than working with model classes to
start with. Because keeping track of these pointers is pretty much impossible.
And you can trust me on that, because I&amp;#8217;ve spend literal days debugging issues
inside the migration framework where pointers where stale pointers were the
cause of a problem.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s an infamous ticket &lt;a class="reference external" href="https://code.djangoproject.com/ticket/23745"&gt;#23745&lt;/a&gt; which is about caching the
rendered model classes. For those of you who&amp;#8217;ve been around long enough, that
is from Django 1.7 onwards, migrations got a significant speed boost in 1.8
with that ticket. There&amp;#8217;s also a lot more into the rendering of
&lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s and &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;s that I&amp;#8217;d love to write about but don&amp;#8217;t
have the time for. Like, for example, figuring out when to evict some cached
model class. Which is enough content for a whole post.&lt;/p&gt;
&lt;h3&gt;Unapplying Migrations&lt;/h3&gt;
&lt;p&gt;Now for the unapplying part: It starts off like the applying part, by
generating a plan to follow. But since &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s can only ever be
mutated _forwards_, the executor will cache all intermediate &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;s
for each migration that will be unapplied. If you&amp;#8217;ve ever encountered a huge
memory load during unapplying migrations, this is why. Once the
&lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;s have been cached, the executor is going to call a
migration&amp;#8217;s &lt;tt class="docutils literal"&gt;unapply()&lt;/tt&gt; method. And it&amp;#8217;s doing that in reverse order of the
plan.&lt;/p&gt;
&lt;h2&gt;Migration Autodetector&lt;/h2&gt;
&lt;img alt="Highlighted the autodetector component in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-09-autodetector.png" /&gt;
&lt;p&gt;As the last trip of our journey through Django&amp;#8217;s migration framework, I want to
briefly look into the &lt;tt class="docutils literal"&gt;MigrationAutodetector&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The autodetector looks at your current models and the &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;
represented by all migrations in your project, and then tries to figure out
what operations need to be added in order to get your current &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt;
to the state that resembles the models in your project.&lt;/p&gt;
&lt;p&gt;At this point, I was originally going to say &amp;#8220;nothing is as simple as that&amp;#8221;.
Because, on the surface, it looks kind of simple. But the devil is in the
details. There&amp;#8217;s this method &lt;tt class="docutils literal"&gt;_detect_changes()&lt;/tt&gt; which synchronously calls a
dozen different methods to generate the changes needed for new and removed
models, added and removed fields, and everything else one can do to a model.
And each of the generation methods more or less works similar: they are
iterating over all models in the &lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; represented by migrations and
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ProjectState``representing&lt;/span&gt; all models in your Django project. And for
each model they then do their thing. For example, they compare the list of
fields on a model and then decide that two fields were added and are not in
your migrations yet. Thus, two ``AddField&lt;/tt&gt; operations will be added. And it&amp;#8217;s
actually these generating functions that hide the complexity. And some of them
are a few hundred lines long.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;img alt="Highlighted all covered components in the diagram" class="responsive-img" src="/images/djangoconeu2020/components-11-highlighted.png" /&gt;
&lt;p&gt;This concludes our journey through Django&amp;#8217;s migration framework. As you&amp;#8217;ve
seen, there are numerous parts involved. And I can only encourage each and
everyone of you to take a look at the code and explore it. And if you&amp;#8217;ve been
hesitant or scared away by its complexity so far, I hope this post made you
curious to learn more.&lt;/p&gt;
&lt;h2&gt;&lt;span class="caps"&gt;FAQ&lt;/span&gt;&lt;/h2&gt;
&lt;h3&gt;Why does changing verbose_name cause a migration?&lt;/h3&gt;
&lt;p&gt;A question that pops up repeatedly is around the field attributes. Such as, why
does changing the &lt;tt class="docutils literal"&gt;verbose_name&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;choices&lt;/tt&gt; cause a migration. With the
post a hand, I hope you now know the answer now: because the &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt; for
the corresponding model changes. And the reason why we can&amp;#8217;t filter out some
attributes is, that nobody knows which attribute somebody may or may not use
inside some migration. Like, &lt;tt class="docutils literal"&gt;choices&lt;/tt&gt; for example, it could be used to
validate data in a data migration.  Even though the attribute doesn&amp;#8217;t have any
effect on any database query in migrations.&lt;/p&gt;
&lt;h3&gt;Why can&amp;#8217;t I remove old field classes?&lt;/h3&gt;
&lt;p&gt;Another question is about old field classes, and why they can&amp;#8217;t be removed. And
again, because of the &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s. If there&amp;#8217;s only one migration in your
Django project that refers to that field, well, the field class needs to stay
around.  You can use migration squashing to possibly get rid of the field. And
if that doesn&amp;#8217;t work automatically, maybe try writing the squashed migration by
hand.  But as long as there&amp;#8217;s a single migration that still imports and uses
the field, the field class needs to stay around.&lt;/p&gt;
&lt;h3&gt;Why doesn&amp;#8217;t the SchemaEditor use ModelStates?&lt;/h3&gt;
&lt;p&gt;When I explained what &amp;#8220;model rendering is&amp;#8221;, one might ask, why does the
&lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt; not &amp;#8220;just work&amp;#8221; with &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s. The answer to that is
two fold.&lt;/p&gt;
&lt;p&gt;Firstly, the &lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt; is part of the database backend and doesn&amp;#8217;t
&amp;#8220;know&amp;#8221; about the internals of the migration framework. Making it work with
&lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s would mean opening up some of the internal &lt;span class="caps"&gt;API&lt;/span&gt;, such as the
&lt;tt class="docutils literal"&gt;ProjectState&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;. Which would not necessarily be an issue,
because the benefits almost certainly outweigh the costs in this case.&lt;/p&gt;
&lt;p&gt;Secondly, because that change needs to happen in a backwards compatible way.
Which is where it gets fairly tricky. It&amp;#8217;s not about the built-in database
backends. The &lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt; has a publicly documented &lt;span class="caps"&gt;API&lt;/span&gt;. We will therefore
need to provide a proper migration path covering one &lt;span class="caps"&gt;LTS&lt;/span&gt; cycle. Which adds a
lot of maintenance burden. But there is ticket &lt;a class="reference external" href="https://code.djangoproject.com/ticket/29898"&gt;#29898&lt;/a&gt; that is precisely about this:
Adapting the &lt;tt class="docutils literal"&gt;SchemaEditor&lt;/tt&gt; to operate on &lt;tt class="docutils literal"&gt;ModelState&lt;/tt&gt;s instead of
rendered models.&lt;/p&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category></entry><entry><title>All Hands on Deck — Handling Security Issues</title><link href="https://markusholtermann.eu/2019/12/all-hands-on-deck-handling-security-issues/" rel="alternate"></link><published>2019-12-06T00:00:00+01:00</published><updated>2019-12-06T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2019-12-06:/2019/12/all-hands-on-deck-handling-security-issues/</id><summary type="html">&lt;p class="first last"&gt;Building a service that exposes millions of user records or is open
to all kinds of security issues is very quickly done. Let&amp;#8217;s look into who&amp;#8217;s
going to fix it?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Building a service that unintentionally exposes millions of people&amp;#8217;s user data
or is open to all kinds of other security issues is, unfortunately, very
quickly done.  Reason enough to look at who&amp;#8217;s going to be involved when
responding to an issue.&lt;/p&gt;
&lt;h2&gt;A Historic Event&lt;/h2&gt;
&lt;p&gt;On April 10th, 1912, the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/RMS_Titanic"&gt;&lt;span class="caps"&gt;R.M.S.&lt;/span&gt; Titanic&lt;/a&gt; on April 10, 1912 was on its maiden
voyage from the port in Southampton to New York City. At that time, the
&lt;em&gt;Titanic&lt;/em&gt; was the largest ocean liner in service. There were about 2,224 people
on board.&lt;/p&gt;
&lt;p&gt;Four days into the journey, on April 14, at about 9 am ship time, &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Edward_Smith_(sea_captain)"&gt;Captain
Edward Smith&lt;/a&gt; received this message from steamship &lt;a class="reference external" href="https://en.wikipedia.org/wiki/RMS_Caronia_(1904)"&gt;Caronia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
West-bound steamers report bergs, growlers, and field ice […]&lt;/blockquote&gt;
&lt;p&gt;A few hours later, at 1:42 pm, this message from steamship &lt;a class="reference external" href="https://en.wikipedia.org/wiki/RMS_Baltic_(1903)"&gt;Baltic&lt;/a&gt; made it to
the captain:&lt;/p&gt;
&lt;blockquote&gt;
[…] Greek steamer &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SS_Athenia_(1903)"&gt;Athenia&lt;/a&gt; reports passing icebergs and large quantities
of field ice today […]&lt;/blockquote&gt;
&lt;p&gt;The captain acknowledged both messages. But it wasn&amp;#8217;t uncommon to see icebergs
and field ice in that area in April. And it was also a general assumption that
ocean liners weren&amp;#8217;t at risk when hitting an ice berg.&lt;/p&gt;
&lt;p&gt;Throughout the day, four more messages were received by the radio operators on
board &lt;em&gt;Titanic&lt;/em&gt;. At 1:45 pm:&lt;/p&gt;
&lt;blockquote&gt;
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/USS_America_(ID-3006)"&gt;Amerika&lt;/a&gt; passed two large icebergs […]&lt;/blockquote&gt;
&lt;p&gt;At 7:30 pm, a message from the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SS_Californian"&gt;Californian&lt;/a&gt; to the &lt;em&gt;Antillian&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
[…] Three large bergs 5 miles southward of us. […]&lt;/blockquote&gt;
&lt;p&gt;And at 9:40 pm from the &lt;em&gt;Mesaba&lt;/em&gt; to the &lt;em&gt;Titanic&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
Ice report […]. Saw much heavy pack ice and great number large icebergs.
Also field ice.&lt;/blockquote&gt;
&lt;p&gt;These three messages got lost and never made it to the captain.&lt;/p&gt;
&lt;p&gt;The sixth and final message which was about to be received by the radio
operators on the &lt;em&gt;Titanic&lt;/em&gt; at 11 pm from the &lt;em&gt;Californian&lt;/em&gt;. The &lt;em&gt;Californian&lt;/em&gt;
was another steam ship which stopped nearby in the middle of the ice. However,
the radio operator on duty at that time was still busy processing a backlog of
messages from the day before. Instead of responding he, shut up the sending
operator with:&lt;/p&gt;
&lt;blockquote&gt;
Shut up! Shut up! I&amp;#8217;m working Cape Race.&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cape_Race"&gt;Cape Race&lt;/a&gt; was a radio station on shore, close by.&lt;/p&gt;
&lt;p&gt;Ten minutes later, at 11:40 pm, after sighting an iceberg right ahead, the
&lt;em&gt;Titanic&lt;/em&gt; hit it starboard. Over the next 2 hours and 40 minutes, more and more
water made its way into the hull.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Titanic&lt;/em&gt; sank at 2:20 am on April 15, 1912, killing more than 1500 people.&lt;/p&gt;
&lt;h2&gt;A Present Company&lt;/h2&gt;
&lt;h3&gt;Receiving Security Issue Report&lt;/h3&gt;
&lt;p&gt;Not as dramatic is receiving a notice of a possible security issue in a tech
company or department. Usually, nobody&amp;#8217;s life is on the line. But it may very
well be in case of — for example — hospitals.&lt;/p&gt;
&lt;p&gt;Support teams at companies receive several messages a day, be it support
requests, spam, or reports of possible security problems. The people who
receive those messages need to sort through them, assess and triage them, and
process them appropriately.&lt;/p&gt;
&lt;p&gt;When their triage of a security issue report is off, the report will not be
classified as a potential security issue. It will instead pile up among the
other bug reports.&lt;/p&gt;
&lt;p&gt;But even when the triaging is right, the report needs to reach the team who is
going to deal with it next: the security team, project managers, or product
managers. And they need to act upon it.&lt;/p&gt;
&lt;h3&gt;Freeing Up Resources&lt;/h3&gt;
&lt;p&gt;Product and project managers will need to budget and plan for engineers to work
on the problem. But the report might also go to a dedicated security team that
will do a more in-depth analysis of the issue and will then communicate with
the product and project managers or fixes it themselves.&lt;/p&gt;
&lt;p&gt;Once assessed, if there is an immediate workaround that mitigates the issue in
the short term, all other customers should be informed accordingly. That will
again involve customer support or key account management.&lt;/p&gt;
&lt;h3&gt;Fixing The Security Issue&lt;/h3&gt;
&lt;p&gt;When the developers came up with a security fix for the issue, other parties in
the engineering department will need to get involved as well: testing and
quality assurance, technical writers and documentarians, and whoever else in
the company that is involved in any regular change to a product.&lt;/p&gt;
&lt;h3&gt;Getting A Fix To Customers&lt;/h3&gt;
&lt;p&gt;Most of the positions mentioned so far are part of the &amp;#8220;Engineering
Department.&amp;#8221; But they are not the only ones involved.&lt;/p&gt;
&lt;p&gt;Looking at proprietary products where customers usually need to pay for an
update, the sales department needs to get involved and decide if they are going
to hand out an update for free or if they are going to charge customers for it
despite the vulnerability. The product managers, in conjunction with the sales
department, should also decide if and how much time and money they invest in
backporting a security fix to older versions that may still be used by
customers.&lt;/p&gt;
&lt;h3&gt;Dealing With The Fallout&lt;/h3&gt;
&lt;p&gt;Imagine one of these leaks that exposed millions of user data records. That&amp;#8217;s a
&lt;span class="caps"&gt;PR&lt;/span&gt; nightmare. The public relations office will have their hands full. And so
does the marketing department. A product slogan like &amp;#8220;&lt;em&gt;your secure thing-y&lt;/em&gt;&amp;#8221; can
quickly become a mockery.&lt;/p&gt;
&lt;p&gt;Furthermore, the legal department is going to be involved. The data protection
officer will need to talk to the authorities and inform them about the leak.&lt;/p&gt;
&lt;p&gt;In the case of a production company, if the bad press about the issue causes
sales of products to go down, then the purchasing department should probably
think about reducing the number of purchased parts.&lt;/p&gt;
&lt;h2&gt;What Do We Learn From The Disaster?&lt;/h2&gt;
&lt;h3&gt;(Re)evaluate Requirements&lt;/h3&gt;
&lt;p&gt;While standards, requirements, guidelines, and such are great things, they can
also be incomplete, incorrect, or inadequate.&lt;/p&gt;
&lt;p&gt;At the time when the &lt;em&gt;Titanic&lt;/em&gt; sank, British vessels over 10,000 tons needed to
carry 16 lifeboats. The &lt;em&gt;Titanic&lt;/em&gt; carried 20 and was thus well above the
requirements. But the lifeboats only provided space for 1.178 people, just
about half of the number of people on board the &lt;em&gt;Titanic&lt;/em&gt;. And only a third of
the total number of people fitting on the &lt;em&gt;Titanic&lt;/em&gt;. But that should have been
fine. Because lifeboats were meant to get people from one ship onto another
that was in close proximity. Nobody expected an ocean liner to sink within 2
hours.&lt;/p&gt;
&lt;p&gt;In today&amp;#8217;s world, just because sending TANs for online banking via &lt;span class="caps"&gt;SMS&lt;/span&gt; is
common practice, it doesn&amp;#8217;t mean it&amp;#8217;s the right thing to do.&lt;/p&gt;
&lt;h3&gt;Practice Makes Perfect&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s also been documented that a lot of crew members had no understanding of
the evacuation procedures. This is not only because of a lack of communication.
But more importantly, because of the lack of practice. Even with 40 years on
the job, captain Smith appeared to be paralyzed when he grasped the enormity of
the problem.&lt;/p&gt;
&lt;p&gt;Translating this into the tech world means we need to train ourselves for the
case of a security incident. A documented procedure is excellent. But without
practice, people will act in all kinds of ways, possibly irrationally.&lt;/p&gt;
&lt;h3&gt;Communication Is Crucial&lt;/h3&gt;
&lt;p&gt;And lastly, and most importantly, communication is crucial.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s been documented that captain Smith ordered his first and second officers
to &amp;#8220;&lt;em&gt;put the women and children in [the lifeboats] and lower away&lt;/em&gt;.&amp;#8221; But the
officers interpreted it differently. One put men next to the woman and children
when no women or children were around. The other one lowered lifeboats with
empty seats.&lt;/p&gt;
&lt;p&gt;As you can see, there can be numerous people involved when it comes to handling
a security issue. And a lot of people mean a lot of communication. Clear
communication is important in any company. But it&amp;#8217;s crucial when it comes to
handling security issues.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;a class="reference external" href="https://archive.org/stream/oceanusv2804wood"&gt;Oceanus magazine, volume 28, number 4, winter 1985/86&lt;/a&gt;. This edition
gives an in-depth insight into what happened around the &lt;em&gt;Titanic&lt;/em&gt; tragedy.&lt;/li&gt;
&lt;li&gt;And &lt;a class="reference external" href="https://www.oreilly.com/library/view/agile-application-security/9781491938836/"&gt;Agile Application Security&lt;/a&gt; by Laura Bell and others. It&amp;#8217;s about how
agile teams can deal and handle security issues. And they can do that despite
their ability to iterate quickly.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/all-hands-on-deck-handling-security-issues-leaddevberlin-2019"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=-Q1sn9J2jiA"&gt;Video recording&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Misc"></category><category term="Incident Handling"></category><category term="LeadDev"></category><category term="Management"></category><category term="Security"></category><category term="Talk"></category><category term="Titanic"></category></entry><entry><title>Logging Rethought</title><link href="https://markusholtermann.eu/2019/04/logging-rethought/" rel="alternate"></link><published>2019-04-12T00:00:00+02:00</published><updated>2019-04-12T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2019-04-12:/2019/04/logging-rethought/</id><summary type="html">&lt;p class="first last"&gt;We build services used by thousands of people. While uptime
monitoring is crucial to deliver a great user experience, understanding
what went wrong is equally important.&lt;/p&gt;
</summary><content type="html">&lt;h2&gt;The Current State Of Logging&lt;/h2&gt;
&lt;p&gt;We build services, potentially used by hundreds, thousands or even millions of
people. And despite all the testing we do, some operations and interactions
with these services will not work out the way we expect or hope. Wouldn&amp;#8217;t it be
great to be able to reconstruct and understand what led to a problem? And then
analyze if the same problem occurred at other times to possibly find the root
cause of the problem?&lt;/p&gt;
&lt;p&gt;Historically, we&amp;#8217;ve used some form of plain text or prose-style logging within
the applications we build and operate. Code like the following is not uncommon:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;Login failed because the connection to the authentication provider &amp;quot;&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; timed out.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_name&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This message is comprehensible and provides a lot of valuable information to a
human reader. The message will either be written to a file, the standard output
or some service that will do something with it.&lt;/p&gt;
&lt;p&gt;Often, these log messages will not be looked at until something is broken.
Typically, at that time, an engineer will need to sort through and scroll
through thousands of lines of text until they eventually find this one message
among a billion other messages:&lt;/p&gt;
&lt;blockquote&gt;
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[2019-04-01T10:47:04.139+00:00]&lt;/span&gt; [&lt;span class="caps"&gt;ERROR&lt;/span&gt;] [srv01] Login failed because the
connection to the authentication provider google timed out.&lt;/tt&gt;&lt;/blockquote&gt;
&lt;p&gt;It is then up to them to understand and deduce that probably a user tried to
log in using credentials for an external authentication provider called
&lt;tt class="docutils literal"&gt;google&lt;/tt&gt;. They can also understand that the login did not succeed because the
connection between the application and the authentication provider was too slow
or otherwise interrupted and thus timed out.&lt;/p&gt;
&lt;h2&gt;Our Logging Is Broken&lt;/h2&gt;
&lt;p&gt;While the message mentioned above is understandable and provides some valuable
information to a human reader, it does not provide any helpful information for
the problem at hand at all. In retrospect, nobody will easily be able to figure
out the problem given this message.&lt;/p&gt;
&lt;p&gt;To understand what information would be helpful, we&amp;#8217;d need to look at the
situation from the perspective of the one dealing with the incident: What &lt;span class="caps"&gt;IP&lt;/span&gt;
and host did the server try to connect to? What was the timeout limit at the
time? How many other attempts were made to talk to that authentication
provider? Where other outgoing connections affected by timeout errors as well?
Were other servers affected by the same problem?&lt;/p&gt;
&lt;h2&gt;Tracing Events&lt;/h2&gt;
&lt;p&gt;The scenario above was rather well constrained: a single application having
trouble talking to some external service. However, most systems these days are
not that simple anymore; they&amp;#8217;re built with a macro-service or microservice
architecture in mind. There are half a dozen to hundreds of individual services
doing work.&lt;/p&gt;
&lt;p&gt;One of the key problems that come with a macro- and microservice architecture
is its complexity in dependencies. One service depends on another that depends
on another that depends on another. When one service causes problems, chances
are, other services are not running smoothly either.&lt;/p&gt;
&lt;p&gt;Often, communication between individual services is handled via &lt;span class="caps"&gt;HTTP&lt;/span&gt; requests.
To better understand how requests — or more generally &lt;em&gt;events&lt;/em&gt; — relate to each
other, what request triggered what other request and what failed request caused
another request to fail, one can &lt;em&gt;tag&lt;/em&gt; each request with a &lt;em&gt;Request &lt;span class="caps"&gt;ID&lt;/span&gt;&lt;/em&gt; or a
&lt;em&gt;Trace &lt;span class="caps"&gt;ID&lt;/span&gt;&lt;/em&gt; and provide that identifier along with log messages. Additionally,
these trace IDs are passed on to sub-requests. This concept is generally known
&lt;strong&gt;Event Tracing&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Tracing Errors&lt;/h3&gt;
&lt;p&gt;When combined with error tracing tools, such as &lt;a class="reference external" href="https://sentry.io/"&gt;Sentry&lt;/a&gt;, attaching a trace &lt;span class="caps"&gt;ID&lt;/span&gt;
to the error event would allow whoever investigates it to look up all log
messages related to that event. And with that additional context, they are
&lt;em&gt;better&lt;/em&gt; equipped in identifying, understanding, and resolving the problem than
without it.&lt;/p&gt;
&lt;h3&gt;There Is More Out There Than &lt;span class="caps"&gt;HTTP&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;But event tracing does not need to stop at the &lt;span class="caps"&gt;HTTP&lt;/span&gt; request/response level.
Messages put into a queue can contain the trace &lt;span class="caps"&gt;ID&lt;/span&gt; as well. This allows workers
processes to pull the trace &lt;span class="caps"&gt;ID&lt;/span&gt; from the message when they start their work and
attach that trace &lt;span class="caps"&gt;ID&lt;/span&gt; to every log message they are writing. When looking at the
collection of log messages for a given trace &lt;span class="caps"&gt;ID&lt;/span&gt; from all different services,
the log messages from workers would show up as well and give viable information
why, for example, some service was talked to or why not.&lt;/p&gt;
&lt;h2&gt;Tracing Users And Other Objects&lt;/h2&gt;
&lt;p&gt;While tracing events, especially across service boundaries, is already a
considerable improvement and significant benefit compared to typical logging
behavior, it&amp;#8217;s not the only thing that can be traced. Anything with an
identifier can be traced.&lt;/p&gt;
&lt;p&gt;To give an example, a request that comes into one service will cause a request
to an authentication service, which will return the authenticated user to the
first service. Log messages within the first service that are written before
the user was authenticated must clearly identify it as such, and so must log
messages after a user was authenticated. That allows for a much easier
understanding of why something failed: because the user in question hadn&amp;#8217;t been
authenticated yet.&lt;/p&gt;
&lt;p&gt;Similarly, one could trace any object by its identifier throughout the entire
service architecture.&lt;/p&gt;
&lt;h2&gt;Adding Structure To Our Logs&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s look back at the log message from above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;Login failed because the connection to the authentication provider &amp;quot;&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; timed out.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_name&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As already mentioned, this log message does not &lt;em&gt;really&lt;/em&gt; give any meaningful
information. It&amp;#8217;s at least missing the target &lt;span class="caps"&gt;IP&lt;/span&gt; address, the timeout limit.
Either can be added easily to the message:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;Login failed because the connection to the authentication provider &amp;quot;&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; @ &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; timed out after &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; ms.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will then result in a message much like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[2019-04-01T10:47:04.139+00:00]&lt;/span&gt; [&lt;span class="caps"&gt;ERROR&lt;/span&gt;] [srv01] Login failed because the
connection to the authentication provider google &amp;#64; 8.8.8.8 timed out after
5 ms.&lt;/tt&gt;&lt;/blockquote&gt;
&lt;p&gt;Now, this log message is already far more helpful. Firstly, the &lt;span class="caps"&gt;IP&lt;/span&gt; of the
authentication provider seems to be 8.8.8.8. Everybody knows this &lt;span class="caps"&gt;IP&lt;/span&gt; address
points to Google&amp;#8217;s public &lt;span class="caps"&gt;DNS&lt;/span&gt; service and not some authentication provider.
Secondly, the timeout is five milliseconds, that&amp;#8217;s hardly enough time for a
&lt;span class="caps"&gt;PING&lt;/span&gt; round-trip on a dodgy WiFi connection (or about 1500km return — 750km one
way).&lt;/p&gt;
&lt;p&gt;The problem here is, though, attaching all this information to some prose-style
messages makes them rather tedious and hard to read and terrible to understand.
Additionally, questions regarding the locality of the problem, its frequency,
etc. are still not easy to answer.&lt;/p&gt;
&lt;p&gt;A solution to solve all these troubles that the traditional logging approaches
seem to have can be &lt;strong&gt;structured logging&lt;/strong&gt;. With structured data, when
appropriately presented to an individual, it contains not only the explicit
information that&amp;#8217;s already part of the message above, but the presentation
itself can convey additional information.&lt;/p&gt;
&lt;p&gt;This post is going to use &lt;a class="reference external" href="https://www.structlog.org/"&gt;structlog&lt;/a&gt; for demonstration purposes.&lt;/p&gt;
&lt;p&gt;Similar to before, the application will need to log events. But instead of
using a prose message an &lt;em&gt;event key&lt;/em&gt; or &lt;em&gt;event identifier&lt;/em&gt; is used &lt;a class="footnote-reference" href="#id2" id="id1"&gt;[1]&lt;/a&gt;.
Additionally, further information is passed along as keyword arguments.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;structlog&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;structlog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;structlog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;auth_provider_failed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;provider_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;provider_ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;provider_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When looking at how the event is recorded internally, it may look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;event&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;auth_provider_failed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1554115624.139&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;provider_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;google&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;provider_ip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;8.8.8.8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;timeout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On top of the data given, there is a &lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt; and a &lt;tt class="docutils literal"&gt;level&lt;/tt&gt; item. They
were added by structlog automatically.&lt;/p&gt;
&lt;p&gt;Now that there is structured data, the event record can be printed out to
something a human can easier grasp. For that, structlog provides a simplistic
&lt;a class="reference external" href="https://www.structlog.org/en/stable/api.html#structlog.processors.KeyValueRenderer"&gt;KeyValueRenderer&lt;/a&gt; and a more fancy, colorizing, &lt;a class="reference external" href="https://www.structlog.org/en/stable/api.html#structlog.dev.ConsoleRenderer"&gt;ConsoleRenderer&lt;/a&gt;. Using
them, once can expect an output that&amp;#8217;s close to this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;2019-04-01&lt;/span&gt; 10:47:04.139 [error&amp;nbsp;&amp;nbsp; ] auth_provider_failed&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [structlog] &lt;span class="pre"&gt;provider_name='google'&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span class="pre"&gt;provider_ip='8.8.8.8'&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; timeout=5&lt;/tt&gt;&lt;/blockquote&gt;
&lt;p&gt;While the &lt;tt class="docutils literal"&gt;KeyValueRenderer&lt;/tt&gt; and the &lt;tt class="docutils literal"&gt;ConsoleRenderer&lt;/tt&gt; are great for
development, they still provide almost unparsable output and thus make the
whole point of having structured logging mood. In deployed environments,
a renderer like the &lt;a class="reference external" href="https://www.structlog.org/en/stable/api.html#structlog.processors.JSONRenderer"&gt;JSONRenderer&lt;/a&gt; will come in handy. It serializes the
dictionary shown above into a &lt;span class="caps"&gt;JSON&lt;/span&gt; string:&lt;/p&gt;
&lt;div class="code-wrapped"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;event&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;auth_provider_failed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1554115624.139&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;provider_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;google&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;provider_ip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;8.8.8.8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;timeout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;h2&gt;&lt;span class="caps"&gt;WYSIWYU&lt;/span&gt; – What You See Is What You Understand&lt;/h2&gt;
&lt;p&gt;As we all know, &lt;span class="caps"&gt;JSON&lt;/span&gt; is a format understood by pretty much every application
out there. When the &lt;span class="caps"&gt;JSON&lt;/span&gt; object is written to a log file that file can be
watched by tools like &lt;a class="reference external" href="https://www.fluentd.org/"&gt;fluentd&lt;/a&gt; or &lt;a class="reference external" href="https://www.elastic.co/products/beats/filebeat"&gt;filebeat&lt;/a&gt;. These services can ingested
the log records into some long-term storage like &lt;a class="reference external" href="https://crate.io/"&gt;CrateDB&lt;/a&gt;. Visualization
tools like &lt;a class="reference external" href="https://grafana.com/"&gt;Grafana&lt;/a&gt; can then be used to turn the events into a graph which a
human eye is better at recognizing than at finding a log message among a
thousand others.&lt;/p&gt;
&lt;p&gt;As an example, when looking at the following graph, one can see that there is
something happening out of the ordinary around the 12:50pm. With timezones in
mind, 12:50pm Berlin time is about the same time as 10:47 &lt;span class="caps"&gt;UTC&lt;/span&gt; on April 1st,
2019, and thus around the time of the event that was recorded above.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="offset-m3 offset-l3 col s12 m6 l6" href="/images/logging-rethought/provider_auth_events.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/logging-rethought/provider_auth_events-483x483-nocrop.jpg" alt="Number successful and failed provider authentication requests." title="Number successful and failed provider authentication requests."&gt;&lt;img class="medium lazyload" data-src="/images/thumb/logging-rethought/provider_auth_events-364x364-nocrop.jpg" alt="Number successful and failed provider authentication requests." title="Number successful and failed provider authentication requests."&gt;&lt;img class="small lazyload" data-src="/images/thumb/logging-rethought/provider_auth_events-480x480-nocrop.jpg" alt="Number successful and failed provider authentication requests." title="Number successful and failed provider authentication requests."&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Without even &lt;em&gt;looking&lt;/em&gt; at any logs, one would be able to say that something
is broken.&lt;/p&gt;
&lt;h3&gt;A Picture Says More Than A Thousand Words&lt;/h3&gt;
&lt;p&gt;Consulting two additional graphs — the first one shows the number of log
messages per log level over time, the second is about the number of started,
finished and failed requests over time — additional information is revealed.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/logging-rethought/log_levels.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/logging-rethought/log_levels-483x483-nocrop.jpg" alt="Number of events per log level." title="Number of events per log level."&gt;&lt;img class="medium lazyload" data-src="/images/thumb/logging-rethought/log_levels-364x364-nocrop.jpg" alt="Number of events per log level." title="Number of events per log level."&gt;&lt;img class="small lazyload" data-src="/images/thumb/logging-rethought/log_levels-480x480-nocrop.jpg" alt="Number of events per log level." title="Number of events per log level."&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/logging-rethought/request_status.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/logging-rethought/request_status-483x483-nocrop.jpg" alt="Number successful, and failed requests." title="Number successful, and failed requests."&gt;&lt;img class="medium lazyload" data-src="/images/thumb/logging-rethought/request_status-364x364-nocrop.jpg" alt="Number successful, and failed requests." title="Number successful, and failed requests."&gt;&lt;img class="small lazyload" data-src="/images/thumb/logging-rethought/request_status-480x480-nocrop.jpg" alt="Number successful, and failed requests." title="Number successful, and failed requests."&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;At some time around 12pm Berlin time, an increase in failed requests and error
messages can be seen. Together with the previous findings, there is a high
likelyhood that the error with the authentication provider at 12:47pm was
caused by something that happened around 12pm.&lt;/p&gt;
&lt;p&gt;As an engineer, this piece of information is vital. Because that at the log
messages around 12pm could then reveal that e.g. a deployment happened, or that
somebody changed some configurtaion value.&lt;/p&gt;
&lt;p&gt;Furthermore, the first graphs also shows that the error about the
authentication provider is not an isolated incident but part of a bigger
problem.&lt;/p&gt;
&lt;h2&gt;Structuring My Django Logs&lt;/h2&gt;
&lt;p&gt;All the above is nice and fancy and shiny and seems to be the unicorn everybody
wishes for. But how can structured logging be used in a real application?&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://gitlab.com/MarkusH/django-structlog"&gt;There is a repository&lt;/a&gt; with a &lt;em&gt;bunch&lt;/em&gt; of different logs on my &lt;a class="reference external" href="https://gitlab.com/MarkusH/django-structlog"&gt;GitLab&lt;/a&gt;
account. It comes with a &lt;a class="reference external" href="https://docs.docker.com/compose/overview/"&gt;docker-compose&lt;/a&gt; setup of a couple of different
services that interact with each other. It comes with a &lt;a class="reference external" href="https://www.fluentd.org/"&gt;fluentd&lt;/a&gt; setup that
writes log messages into a &lt;a class="reference external" href="https://crate.io/"&gt;CrateDB&lt;/a&gt; instance. And a &lt;a class="reference external" href="https://grafana.com/"&gt;Grafana&lt;/a&gt; dashboard
showing some graphs.&lt;/p&gt;
&lt;h2&gt;Logging For European Users&lt;/h2&gt;
&lt;p&gt;When logging information, potentially data about users and customers ends up in
those logs. Since th &lt;span class="caps"&gt;EU&lt;/span&gt; has graciously provided the beloved &lt;a class="reference external" href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation"&gt;&lt;span class="caps"&gt;GDPR&lt;/span&gt;&lt;/a&gt;, there is
obviously no way to just log whatever one wants.&lt;/p&gt;
&lt;p&gt;To keep this section short, a few tips on what to avoid when logging:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Never log any secrets. &lt;strong&gt;&lt;span class="caps"&gt;EVER&lt;/span&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Ensure your log data store is secured and not world-readable! &lt;strong&gt;&lt;span class="caps"&gt;NO&lt;/span&gt;, &lt;span class="caps"&gt;REALLY&lt;/span&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Explicitly log whatever should end up in logs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# DO&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;some_event&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lorem&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ipsum&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# DON&amp;#39;T DO&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;some_event&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;some_object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Come up with a naming scheme and convention for event names before you begin.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Rather log a bit too much than too little.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Log object IDs as well as their attributes where it makes sense. The
attributes may change over time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Improve as you learn. Add missing information when you notice it would be
great to have it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table class="docutils footnote" frame="void" id="id2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;It&amp;#8217;s usually a good idea to think about the different events that
could be logged now and in the future and to come up with a naming scheme
and naming convention before logging events.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Misc"></category><category term="Cluster"></category><category term="Database"></category><category term="Django"></category><category term="Logging"></category><category term="Python"></category><category term="Security"></category></entry><entry><title>Less Obvious Things To Do With Django’s ORM</title><link href="https://markusholtermann.eu/2019/03/less-obvious-things-to-do-with-djangos-orm/" rel="alternate"></link><published>2019-03-23T00:00:00+01:00</published><updated>2019-03-23T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2019-03-23:/2019/03/less-obvious-things-to-do-with-djangos-orm/</id><summary type="html">&lt;p class="first last"&gt;Django&amp;#8217;s &lt;span class="caps"&gt;ORM&lt;/span&gt; is powerful and has been for ages. It only became even
more powerful over the last years. In this post I want to explore some
less-known features.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Django&amp;#8217;s &lt;span class="caps"&gt;ORM&lt;/span&gt; is powerful. And it has been for ages. It only became even more
powerful over the last years. Django&amp;#8217;s &lt;span class="caps"&gt;ORM&lt;/span&gt; can be intimidating because it can
do so many things. And it can also be confusing when trying to find out how to
do things.&lt;/p&gt;
&lt;h2&gt;Our Models&lt;/h2&gt;
&lt;p&gt;Before we begin with the examples we need to define our database layout.
Throughout this post I will be using these three database models:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManyToManyField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Genre&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can input whatever data you want. My &lt;a class="reference external" href="https://github.com/MarkusH/talk-orm/"&gt;talk-orm repository&lt;/a&gt; on GitHub
contains the full Django project as well as some sample data I sourced from
&lt;a class="reference external" href="https://www.goodreads.com/"&gt;Goodreads&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Counting objects&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s start with something basic. We&amp;#8217;ll count how many records we have in the
database:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;literature.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Genre&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;1142&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;3578&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;412&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We access the attribute &lt;tt class="docutils literal"&gt;objects&lt;/tt&gt; on a database model class. &lt;tt class="docutils literal"&gt;objects&lt;/tt&gt; is a
&amp;#8220;Model Manager&amp;#8221;. Django uses them to connect database model classes and
&lt;tt class="docutils literal"&gt;QuerySet&lt;/tt&gt;s. We then access the &lt;tt class="docutils literal"&gt;count()&lt;/tt&gt; method on a manager which uses
the underlying, auto-generated queryset to create the &lt;span class="caps"&gt;SQL&lt;/span&gt; that returns the
number of records for that model.&lt;/p&gt;
&lt;h2&gt;Fetching single objects&lt;/h2&gt;
&lt;p&gt;Great, let&amp;#8217;s fetch some random author from the database:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;console&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;.../django/db/models/manager.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manager_method&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_queryset&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;.../django/db/models/query.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;literature&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MultipleObjectsReturned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;than&lt;/span&gt;
&lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt; &lt;span class="mi"&gt;1142&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, that obviously didn&amp;#8217;t go as planned. We got an exception that multiple
objects got returned. That&amp;#8217;s what &lt;tt class="docutils literal"&gt;get()&lt;/tt&gt; is all about. It returns
&lt;strong&gt;exactly&lt;/strong&gt; one object. Not zero, not two or more. One!&lt;/p&gt;
&lt;p&gt;Alternatively, we could use &lt;tt class="docutils literal"&gt;first()&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bill&lt;/span&gt; &lt;span class="n"&gt;Bryson&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The difference here, &lt;tt class="docutils literal"&gt;first()&lt;/tt&gt; may return &lt;tt class="docutils literal"&gt;None&lt;/tt&gt; in case there is no record
to return. We can see that when we try to select me as an author:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Markus&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Markus&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;console&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;.../django/db/models/query.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;399&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;
&lt;span class="n"&gt;literature&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoesNotExist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="n"&gt;matching&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While &lt;tt class="docutils literal"&gt;first()&lt;/tt&gt; returned &lt;tt class="docutils literal"&gt;None&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;get()&lt;/tt&gt; raised a &lt;tt class="docutils literal"&gt;DoesNotExist&lt;/tt&gt;
exception.&lt;/p&gt;
&lt;h2&gt;Advanced filtering&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;ve just seen how we can filter on the exact value of a model field when
querying the database. But there&amp;#8217;s more. For example, we can select all authors
whose name starts with &lt;tt class="docutils literal"&gt;&amp;quot;Lisa&amp;quot;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name__startswith&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Lisa&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lisa&lt;/span&gt; &lt;span class="n"&gt;Unger&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lisa&lt;/span&gt; &lt;span class="n"&gt;Scottoline&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Similarly, when we don&amp;#8217;t care about upper and lower case and only care if the
character sequence is part of a record, we can use &lt;tt class="docutils literal"&gt;__icontains&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name__icontains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tom&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Robert&lt;/span&gt; &lt;span class="n"&gt;Tombs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Tom&lt;/span&gt; &lt;span class="n"&gt;Barbash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Tom&lt;/span&gt; &lt;span class="n"&gt;Sweterlitsch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All these &lt;a class="reference external" href="https://docs.djangoproject.com/en/2.1/ref/models/querysets/#field-lookups"&gt;Field Lookups&lt;/a&gt; are fully documented.&lt;/p&gt;
&lt;h2&gt;Following related objects&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s say we want to print a list of all books with their corresponding author.
The first approach we will make may very well look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -- Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Harry&lt;/span&gt; &lt;span class="n"&gt;Potter&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Sorcerer&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s Stone -- Author: J.K. Rowling&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Harry&lt;/span&gt; &lt;span class="n"&gt;Potter&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Prisoner&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Azkaban&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Rowling&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Short&lt;/span&gt; &lt;span class="n"&gt;History&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Nearly&lt;/span&gt; &lt;span class="n"&gt;Everything&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bill&lt;/span&gt; &lt;span class="n"&gt;Bryson&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Lord&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Rings&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Tolkien&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Fellowship&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Ring&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Tolkien&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;One&lt;/span&gt; &lt;span class="n"&gt;Hundred&lt;/span&gt; &lt;span class="n"&gt;Years&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Solitude&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Gabriel&lt;/span&gt; &lt;span class="n"&gt;García&lt;/span&gt; &lt;span class="n"&gt;Márquez&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Angels&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Demons&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dan&lt;/span&gt; &lt;span class="n"&gt;Brown&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That works, we end up with &lt;em&gt;a lot&lt;/em&gt; of database queries. Specifically, we end up
with &lt;tt class="docutils literal"&gt;1 + $number_of_books&lt;/tt&gt; queries. Why is that?&lt;/p&gt;
&lt;p&gt;First, we&amp;#8217;re selecting all the books. That&amp;#8217;s one query. Then, in the for loop,
we make &lt;em&gt;one query per book&lt;/em&gt;. In case you&amp;#8217;re wondering: that is &lt;em&gt;bad&lt;/em&gt;! It may
be &amp;#8220;just fine&amp;#8221; for two to five books, but when you have more, you &lt;em&gt;will&lt;/em&gt; end up
with performance problems.&lt;/p&gt;
&lt;h3&gt;Sidebar: Inspecting database queries&lt;/h3&gt;
&lt;p&gt;When you&amp;#8217;re developing your Django project or app, it can be helpful to check
the recent database queries quickly. For that, Django tracks them on the
database connection:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queries&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SELECT COUNT(*) AS &amp;quot;__count&amp;quot; FROM &amp;quot;literature_author&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SELECT COUNT(*) AS &amp;quot;__count&amp;quot; FROM &amp;quot;literature_book&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Following &lt;cite&gt;one-to-one&lt;/cite&gt; and &lt;cite&gt;many-to-one&lt;/cite&gt; relationships&lt;/h3&gt;
&lt;p&gt;Getting back to where we left off, we need to find a way to optimize our &lt;tt class="docutils literal"&gt;1 +
$number_of_books&lt;/tt&gt; database queries. And Django has just the right tool for
that: &lt;tt class="docutils literal"&gt;select_related()&lt;/tt&gt;. This queryset method tells Django to fetch
&lt;em&gt;forward relationships&lt;/em&gt; when making the initial query:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -- Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Harry&lt;/span&gt; &lt;span class="n"&gt;Potter&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Sorcerer&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s Stone -- Author: J.K. Rowling&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Harry&lt;/span&gt; &lt;span class="n"&gt;Potter&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Prisoner&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Azkaban&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Rowling&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Short&lt;/span&gt; &lt;span class="n"&gt;History&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Nearly&lt;/span&gt; &lt;span class="n"&gt;Everything&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bill&lt;/span&gt; &lt;span class="n"&gt;Bryson&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Lord&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Rings&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Tolkien&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Fellowship&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Ring&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Tolkien&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;One&lt;/span&gt; &lt;span class="n"&gt;Hundred&lt;/span&gt; &lt;span class="n"&gt;Years&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Solitude&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Gabriel&lt;/span&gt; &lt;span class="n"&gt;García&lt;/span&gt; &lt;span class="n"&gt;Márquez&lt;/span&gt;
&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Angels&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Demons&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dan&lt;/span&gt; &lt;span class="n"&gt;Brown&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we have only 1 query. Exactly what we wanted.&lt;/p&gt;
&lt;p&gt;I wrote above that &lt;tt class="docutils literal"&gt;select_related()&lt;/tt&gt; is for &lt;em&gt;forward relationships&lt;/em&gt;. That
means, it only ever works when there is at most one object on the other end of
the relationships. &amp;#8220;At most,&amp;#8221; because that related object could also be
&lt;tt class="docutils literal"&gt;None&lt;/tt&gt;, e.g. when you have a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; with &lt;tt class="docutils literal"&gt;null=True&lt;/tt&gt;. In other
words, you can use &lt;tt class="docutils literal"&gt;select_related()&lt;/tt&gt; when the current model has a
&lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;OneToOneField&lt;/tt&gt;, or if the current model is the opposite
end of an &lt;tt class="docutils literal"&gt;OneToOneField&lt;/tt&gt;. It will &lt;strong&gt;not&lt;/strong&gt; work for &lt;tt class="docutils literal"&gt;ManyToManyFields&lt;/tt&gt; or
the reverse of a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt;.&lt;/p&gt;
&lt;h3&gt;Following &lt;cite&gt;one-to-many&lt;/cite&gt; and &lt;cite&gt;many-to-many&lt;/cite&gt; relationships&lt;/h3&gt;
&lt;p&gt;When there are &lt;cite&gt;one-to-one&lt;/cite&gt; and &lt;cite&gt;many-to-one&lt;/cite&gt; relationships, there are probably
&lt;cite&gt;one-to-many&lt;/cite&gt; and &lt;cite&gt;many-to-many&lt;/cite&gt; as well. And indeed, there are. You use them
when you have &lt;tt class="docutils literal"&gt;ManyToManyFields&lt;/tt&gt; or when you follow a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt;
backward.&lt;/p&gt;
&lt;p&gt;Given our database schema, we have that scenario when we want to list all the
books for each author. The naïve approach will look a bit like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  - Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bill&lt;/span&gt; &lt;span class="n"&gt;Bryson&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Short&lt;/span&gt; &lt;span class="n"&gt;History&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Nearly&lt;/span&gt; &lt;span class="n"&gt;Everything&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Edith&lt;/span&gt; &lt;span class="n"&gt;Wharton&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ethan&lt;/span&gt; &lt;span class="n"&gt;Frome&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Patricia&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;McKillip&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Forests&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Serre&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Bards&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Bone&lt;/span&gt; &lt;span class="n"&gt;Plain&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you might imagine, this has similar problems as the example I had above. We
now have &lt;tt class="docutils literal"&gt;1 + $number_of_authors&lt;/tt&gt; queries: one for the list of authors, and
one for each author to get the books. We can optimize this to exactly two
database queries:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  - title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bill&lt;/span&gt; &lt;span class="n"&gt;Bryson&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Short&lt;/span&gt; &lt;span class="n"&gt;History&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Nearly&lt;/span&gt; &lt;span class="n"&gt;Everything&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Edith&lt;/span&gt; &lt;span class="n"&gt;Wharton&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ethan&lt;/span&gt; &lt;span class="n"&gt;Frome&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Patricia&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;McKillip&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Forests&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Serre&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Bards&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Bone&lt;/span&gt; &lt;span class="n"&gt;Plain&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Django will construct a &lt;em&gt;prefetch query&lt;/em&gt; under the hood which pretty much
equals to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author_id__in&lt;/span&gt;&lt;span class="o"&gt;=...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The filter on &lt;tt class="docutils literal"&gt;author_id&lt;/tt&gt; will automatically be populated by Django and limit
the books to the set of authors selected in the first query.&lt;/p&gt;
&lt;p&gt;If you want to limit the books queryset further, you can do so using
&lt;tt class="docutils literal"&gt;Prefetch&lt;/tt&gt; objects:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Prefetch&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;prefetch_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title__startswith&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;Prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prefetch_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Aggregating data&lt;/h2&gt;
&lt;p&gt;All the things above are somewhat basic and something everybody using Django
will come across eventually. The next topic about data aggregation and
annotating database objects with additional information is something that&amp;#8217;s
still common, but it may take some time for this to come up in a project.&lt;/p&gt;
&lt;h3&gt;Let&amp;#8217;s &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;&lt;/tt&gt; again&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s start by counting the number of books per author:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;book_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Jen&lt;/span&gt; &lt;span class="n"&gt;Wang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sarah&lt;/span&gt; &lt;span class="n"&gt;MacLean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Charles&lt;/span&gt; &lt;span class="n"&gt;Soule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;King&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Jesmyn&lt;/span&gt; &lt;span class="n"&gt;Ward&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Victor&lt;/span&gt; &lt;span class="n"&gt;LaValle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And at this point, it&amp;#8217;s interesting to start to look at the &lt;span class="caps"&gt;SQL&lt;/span&gt; Django
generated:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;book_count&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The critical puzzle piece in this &lt;span class="caps"&gt;SQL&lt;/span&gt; statement is the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JOIN&lt;/span&gt;&lt;/tt&gt; between the
author and book tables with the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;&lt;/tt&gt; in the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/tt&gt; clause. Django
shifts the entire work to calculate the sum of books per author to the
database.&lt;/p&gt;
&lt;h3&gt;Let&amp;#8217;s &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SUM&lt;/span&gt;&lt;/tt&gt; it up&lt;/h3&gt;
&lt;p&gt;What we have is already great. Now, let&amp;#8217;s look into finding the top five
authors with the most votes across all books:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sum&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;sum_votes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books__votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-sum_votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum_votes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Rowling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10354107&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Suzanne&lt;/span&gt; &lt;span class="n"&gt;Collins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10270371&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Rick&lt;/span&gt; &lt;span class="n"&gt;Riordan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5860434&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;John&lt;/span&gt; &lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5694398&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Stephen&lt;/span&gt; &lt;span class="n"&gt;King&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5181285&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead of a simple count, we sum up all the votes for all books an author has
written. We then sort the authors by the sum of votes in decreasing order and
limit the amount of records returned from the database to five.&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;SQL&lt;/span&gt; for this query is not too different to the previous one. Instead of
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;COUNT&lt;/span&gt;&lt;/tt&gt; there is a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SUM&lt;/span&gt;&lt;/tt&gt; function. There is now a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;ORDER&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;&lt;/tt&gt; column with
a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;LIMIT&lt;/span&gt;&lt;/tt&gt;. All as expected.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;sum_votes&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_author&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;sum_votes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Annotating &amp;#8220;arbitrary&amp;#8221; data&lt;/h3&gt;
&lt;p&gt;The annotations shown above are not the only thing Django can do. There&amp;#8217;s a lot
more:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models.functions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Substr&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;substr_exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;StrIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;output_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;substr_exp&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;book_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-book_count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Stephen&lt;/span&gt; &lt;span class="n"&gt;King&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Peter&lt;/span&gt; &lt;span class="n"&gt;Meredith&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This query will count the books per author, but will also attach the &amp;#8220;first
name&amp;#8221; to each model instance.&lt;/p&gt;
&lt;p&gt;If we add a &lt;tt class="docutils literal"&gt;values()&lt;/tt&gt; queryset method after the first &lt;tt class="docutils literal"&gt;annotate()&lt;/tt&gt; method
we effectively group on the first name for the counting:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;substr_exp&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s2"&gt;&amp;quot;first_name&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;book_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-book_count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Lisa&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;book_count&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;David&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;book_count&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m well aware that this is a lot to digest and understand. The Django
documentation has a &lt;a class="reference external" href="https://docs.djangoproject.com/en/2.1/topics/db/aggregation/"&gt;whole chapter on aggregations&lt;/a&gt; that I can highly
recommend to read through and have a look at whenever you need to deal with
aggregations and annotations, because I haven&amp;#8217;t even covered half of it.&lt;/p&gt;
&lt;h2&gt;Top-k selects&lt;/h2&gt;
&lt;p&gt;The last thing I want to cover is something that&amp;#8217;s generally not easy to
express in &lt;span class="caps"&gt;SQL&lt;/span&gt; and also computational wise rather heavy. It&amp;#8217;s about selecting
the &lt;em&gt;top-k&lt;/em&gt; elements for something else.&lt;/p&gt;
&lt;p&gt;The typical approach to this problem, across all databases, is the use of
&lt;em&gt;subqueries&lt;/em&gt;. A subquery is a full &lt;span class="caps"&gt;SQL&lt;/span&gt; query that will run as part of a &amp;#8220;main&amp;#8221;
database query. Some database have additional capabilities, though, that make
&lt;em&gt;top-k&lt;/em&gt; selection relatively easy. On PostgreSQL, that would be a &lt;a class="reference external" href="https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-LATERAL"&gt;&lt;span class="caps"&gt;LATERAL&lt;/span&gt;
&lt;span class="caps"&gt;JOIN&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s start by first selecting the top three books by the number of votes per
author, and then the top three books by votes per genre.&lt;/p&gt;
&lt;h3&gt;Top three by author&lt;/h3&gt;
&lt;p&gt;When we discussed the &lt;tt class="docutils literal"&gt;prefetch_related&lt;/tt&gt; method before, we already looked
into the &lt;tt class="docutils literal"&gt;Prefetch&lt;/tt&gt; object. We will leverage that here. Let&amp;#8217;s build this
query piece by piece.&lt;/p&gt;
&lt;p&gt;We want authors and a set of books that belong to each author. For now, the
&lt;tt class="docutils literal"&gt;books_qs&lt;/tt&gt; won&amp;#8217;t be doing much:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;Prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;books_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this, we will prefetch all books that belong to an author. As a next step,
let&amp;#8217;s sort the list of books by votes in descending order:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last step is to limit the number of books to &lt;em&gt;k&lt;/em&gt;. The approach everybody
takes will be this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, this will cause Django to raise an &lt;tt class="docutils literal"&gt;AssertionError&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;Prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;books_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="ne"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cannot&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;once&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;slice&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;been&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If one thinks about that, Django will take the &lt;tt class="docutils literal"&gt;book_qs&lt;/tt&gt; and apply a
&lt;tt class="docutils literal"&gt;filter()&lt;/tt&gt; call on the &lt;tt class="docutils literal"&gt;author_id&lt;/tt&gt; to limit the books to the list of
authors selected before. So, we need another approach. There&amp;#8217;s already a
&lt;a class="reference external" href="https://code.djangoproject.com/ticket/26780"&gt;feature request ticket&lt;/a&gt; on the Django bug tracker.&lt;/p&gt;
&lt;p&gt;Instead, we need to look into &lt;tt class="docutils literal"&gt;Subquery&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;OuterRef&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;First, we&amp;#8217;ll select the primary key of the top &lt;em&gt;k&lt;/em&gt; books while filtering on an
&lt;em&gt;outer reference&lt;/em&gt; to an &lt;tt class="docutils literal"&gt;author_id&lt;/tt&gt;. This queryset will not work on its own.
It will only ever work in the context of a subquery that knows about an
&lt;tt class="docutils literal"&gt;author_id&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;We then put that &lt;tt class="docutils literal"&gt;book_sub_qs&lt;/tt&gt; into a subquery. With that, the inner query
&amp;#8220;knows&amp;#8221; about the &lt;tt class="docutils literal"&gt;author_id&lt;/tt&gt;. If we were to iterate over &lt;tt class="docutils literal"&gt;books_qs&lt;/tt&gt;, we&amp;#8217;d
get a list of books, the first book having the most votes, and not more than 3
books per author:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OuterRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Subquery&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_sub_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;author_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OuterRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;pk__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Subquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_sub_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With that, we can now go back to our &lt;tt class="docutils literal"&gt;Prefetch()&lt;/tt&gt; object and combine authors
and books:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_sub_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;author_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OuterRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;pk__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Subquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_sub_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;Prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;books_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Doris&lt;/span&gt; &lt;span class="n"&gt;Kearns&lt;/span&gt; &lt;span class="n"&gt;Goodwin&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Team&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Rivals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Political&lt;/span&gt; &lt;span class="n"&gt;Genius&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Abraham&lt;/span&gt; &lt;span class="n"&gt;Lincoln&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;Ordinary&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Franklin&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Eleanor&lt;/span&gt; &lt;span class="n"&gt;Roosevelt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Home&lt;/span&gt; &lt;span class="n"&gt;Front&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt; &lt;span class="n"&gt;War&lt;/span&gt; &lt;span class="n"&gt;II&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Bully&lt;/span&gt; &lt;span class="n"&gt;Pulpit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Theodore&lt;/span&gt; &lt;span class="n"&gt;Roosevelt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;William&lt;/span&gt; &lt;span class="n"&gt;Howard&lt;/span&gt; &lt;span class="n"&gt;Taft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Golden&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Journalism&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Nathaniel&lt;/span&gt; &lt;span class="n"&gt;Philbrick&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Heart&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Sea&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Tragedy&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Whaleship&lt;/span&gt; &lt;span class="n"&gt;Essex&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Mayflower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Story&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Courage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Community&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;War&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Last&lt;/span&gt; &lt;span class="n"&gt;Stand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Custer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sitting&lt;/span&gt; &lt;span class="n"&gt;Bull&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Battle&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Little&lt;/span&gt; &lt;span class="n"&gt;Bighorn&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Terry&lt;/span&gt; &lt;span class="n"&gt;Pratchett&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Blink&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Collected&lt;/span&gt; &lt;span class="n"&gt;Shorter&lt;/span&gt; &lt;span class="n"&gt;Fiction&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Timothy&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Tyson&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Blood&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Emmett&lt;/span&gt; &lt;span class="n"&gt;Till&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Harper&lt;/span&gt; &lt;span class="n"&gt;Lee&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;To&lt;/span&gt; &lt;span class="n"&gt;Kill&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Mockingbird&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Go&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Watchman&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mark&lt;/span&gt; &lt;span class="n"&gt;Kurlansky&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Salt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt; &lt;span class="n"&gt;History&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Cod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;Biography&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Fish&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;Changed&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Basque&lt;/span&gt; &lt;span class="n"&gt;History&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Story&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Nation&lt;/span&gt;
&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;James&lt;/span&gt; &lt;span class="n"&gt;Hilton&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;Harvest&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt;
            &lt;span class="n"&gt;U0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;U0&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt;
            &lt;span class="n"&gt;U0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
            &lt;span class="n"&gt;U0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
        &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="mi"&gt;18885860&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Top three by genre&lt;/h3&gt;
&lt;p&gt;We can use the very same pattern we have above for top-k by an author when we
want to select the top-k by genre.&lt;/p&gt;
&lt;p&gt;The notable difference between the Book-Author and Book-Genre relationship is
that one of them is a many-to-one (Book-Author) and the other one is
many-to-many (Book-Genre).&lt;/p&gt;
&lt;p&gt;Due to the relationship being a many-to-many one, we need to make one change
to remove duplicate books — the &lt;tt class="docutils literal"&gt;book_qs&lt;/tt&gt; gains a &lt;tt class="docutils literal"&gt;distinct()&lt;/tt&gt; call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_sub_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;genres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OuterRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;genres&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pk&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book_qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;pk__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Subquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_sub_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-votes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prefetch_related&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;Prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;books&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;book_qs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;genres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Genre: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Fiction&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Harry&lt;/span&gt; &lt;span class="n"&gt;Potter&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Sorcerer&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s Stone&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Hunger&lt;/span&gt; &lt;span class="n"&gt;Games&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;To&lt;/span&gt; &lt;span class="n"&gt;Kill&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Mockingbird&lt;/span&gt;
&lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Romance&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Hunger&lt;/span&gt; &lt;span class="n"&gt;Games&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Fault&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Our&lt;/span&gt; &lt;span class="n"&gt;Stars&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Pride&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Prejudice&lt;/span&gt;
&lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cultural&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Poisonwood&lt;/span&gt; &lt;span class="n"&gt;Bible&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Things&lt;/span&gt; &lt;span class="n"&gt;Fall&lt;/span&gt; &lt;span class="n"&gt;Apart&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Paris&lt;/span&gt; &lt;span class="n"&gt;Wife&lt;/span&gt;
&lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Australia&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Forgotten&lt;/span&gt; &lt;span class="n"&gt;Garden&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;Dry&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Force&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;Nature&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book_genres&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;genre_id&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;_prefetch_related_val_genre_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;author_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book_genres&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book_genres&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;book_id&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book_genres&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;T4&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;book_id&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt;
            &lt;span class="n"&gt;U0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;U0&lt;/span&gt;
        &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book_genres&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;U1&lt;/span&gt;
            &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;U0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;U1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;book_id&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt;
            &lt;span class="n"&gt;U1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;genre_id&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;literature_book_genres&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;genre_id&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
            &lt;span class="n"&gt;U0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
        &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;T4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;genre_id&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="mi"&gt;411&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;literature_book&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;votes&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="Development"></category><category term="Database"></category><category term="Django"></category><category term="ORM"></category><category term="SQL"></category></entry><entry><title>Setting up a private &amp; secure IRC channel</title><link href="https://markusholtermann.eu/2018/11/private_secure_irc_channels/" rel="alternate"></link><published>2018-11-03T00:00:00+01:00</published><updated>2018-11-03T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2018-11-03:/2018/11/private_secure_irc_channels/</id><summary type="html">&lt;p class="first last"&gt;While over 30y old, &lt;span class="caps"&gt;IRC&lt;/span&gt; is still used today. A lot because of it&amp;#8217;s
reliability and configuration options. But those make setting up a private
&lt;span class="caps"&gt;IRC&lt;/span&gt; channels hard.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Internet_Relay_Chat"&gt;Internet Relay Chat (&lt;span class="caps"&gt;IRC&lt;/span&gt;)&lt;/a&gt;
was create in 1988 by Jarkko Oikarinen. And it&amp;#8217;s stil around and used. At least
today there are a ton of configuration options one has at their disposal to
configure their nick, channels, or their client.&lt;/p&gt;
&lt;p&gt;In this post I&amp;#8217;ll show you how you can create and register a private and
secured &lt;span class="caps"&gt;IRC&lt;/span&gt; channel that restricts who has access and can post to it.&lt;/p&gt;
&lt;p&gt;This post assumes that you have registered and authenticated at the &lt;span class="caps"&gt;IRC&lt;/span&gt;
network.&lt;/p&gt;
&lt;p&gt;First things first, you need to create a channel. Let&amp;#8217;s call it &lt;tt class="docutils literal"&gt;#project&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/join #project
&lt;/pre&gt;
&lt;p&gt;You then want to make sure that channel is registered to yourself:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/msg ChanServ REGISTER #project
&lt;/pre&gt;
&lt;p&gt;You also want to enable the &lt;tt class="docutils literal"&gt;ChanServ&lt;/tt&gt; guard so you can reclaim access and
have it keep the channel in the state you want it to be, if you ever are kicked
out:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/msg ChanServ SET #project GUARD ON
&lt;/pre&gt;
&lt;p&gt;Now, you need to define who has &lt;em&gt;full&lt;/em&gt; access to that channel. That means they
could add others who have full permissions. And they&amp;#8217;d be able to kick you out
and revoke your access:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/msg ChanServ FLAGS #project USERNAME +AFRefiorstv
&lt;/pre&gt;
&lt;p&gt;Next up, you can hide the channel from channel lists and user profiles. We&amp;#8217;ll
also enable a verbose logging mode that shows who changed what:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/msg ChanServ SET #project PRIVATE ON
/msg ChanServ SET #project VERBOSE ON
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ll then set a couple of channel modes. These are &amp;#8212; in order &amp;#8212; &lt;span class="caps"&gt;SSL&lt;/span&gt; only,
no color, members can invite, invite only, no external messages, private,
block unidentified, secret:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/msg ChanServ SET #project MLOCK +Scginprs
&lt;/pre&gt;
&lt;p&gt;At this point, nobody else than the people you added above, will have access.
To change that you&amp;#8217;ll need to add them to the invite list. Replace the
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;USERNAME&lt;/span&gt;&lt;/tt&gt; part only. The &lt;tt class="docutils literal"&gt;$a:&lt;/tt&gt; indicates it&amp;#8217;s an account and needs to be
part of this statement:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/mode #project +I $a:USERNAME
&lt;/pre&gt;
&lt;p&gt;Lastly, you may want to check the invite list again:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/mode #project +I
&lt;/pre&gt;
</content><category term="Misc"></category><category term="IRC"></category><category term="Security"></category></entry><entry><title>Simple TOTP Bash Script</title><link href="https://markusholtermann.eu/2018/08/simple-bash-totp-script/" rel="alternate"></link><published>2018-08-10T00:00:00+02:00</published><updated>2018-08-10T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2018-08-10:/2018/08/simple-bash-totp-script/</id><summary type="html">&lt;p class="first last"&gt;Most &lt;span class="caps"&gt;TOTP&lt;/span&gt; / &lt;span class="caps"&gt;2FA&lt;/span&gt; clients are for smartphones. There&amp;#8217;s not really an
easy / simple one for Linux. Here&amp;#8217;s one in Bash&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Using Two Factor Authentication (&lt;span class="caps"&gt;2FA&lt;/span&gt;) for services is a good idea. Most
services will require you to have an Android or iOS smartphone and use Google
Authenticator or similar apps to generate &lt;span class="caps"&gt;TOTP&lt;/span&gt; codes.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s fine if you have a smartphone. If you don&amp;#8217;t have one you can often use
&lt;span class="caps"&gt;SMS&lt;/span&gt; tokens instead. But &lt;span class="caps"&gt;SMS&lt;/span&gt; for &lt;span class="caps"&gt;2FA&lt;/span&gt; is not recommended anymore. Also, what do
you do if you don&amp;#8217;t have a smartphone or mobile phone at all? I know of at
least 2 people who need to use &lt;span class="caps"&gt;2FA&lt;/span&gt; and don&amp;#8217;t have a smartphone.&lt;/p&gt;
&lt;p&gt;First thing I suggested to them: use Authy, there&amp;#8217;s a Chrome plugin you can use
instead of a smartphone. But it turns out, they seem to require you to have a
smartphone. I guess for backup or security reasons.&lt;/p&gt;
&lt;p&gt;Since both people have access to a Linux system, I figured I might as well find
a Linux client that allows them to generate TOTPs. But all tools or scripts I
could find seemed bloated. Until &lt;a class="reference external" href="https://twitter.com/EmmaDelescolle/status/1027498473590018049"&gt;Emma Delescolle pointed&lt;/a&gt; me to
&lt;a class="reference external" href="https://www.nongnu.org/oath-toolkit/"&gt;oath-toolkit&lt;/a&gt;. Thanks!&lt;/p&gt;
&lt;p&gt;With the included &lt;tt class="docutils literal"&gt;oathtool&lt;/tt&gt; one can generate a &lt;span class="caps"&gt;TOTP&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ oathtool --base32 --totp ONSWG4TFOQYTEMZUGU3DOOBZ
&lt;span class="m"&gt;785263&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great. Now I only needed a way to securely store the secret. I figured a
symmetrically encrypted file using GnuPG would probably do just fine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ &lt;span class="nb"&gt;echo&lt;/span&gt; ONSWG4TFOQYTEMZUGU3DOOBZ &lt;span class="p"&gt;|&lt;/span&gt; gpg --symmetric --out ~/.secret-file
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And loading that file isn&amp;#8217;t hard either:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ &lt;span class="nv"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;gpg &amp;lt; ~/.secret-file&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
$ oathtool --base32 --totp &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="m"&gt;785263&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great. Now I only needed to tie those things together in a small script that
lets me add new secrets for various services and gives me a &lt;span class="caps"&gt;TOTP&lt;/span&gt; back when I
need one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c1"&gt;# Copyright (c) 2018, info AT markusholtermann DOT eu&lt;/span&gt;
&lt;span class="c1"&gt;# All rights reserved.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Redistribution and use in source and binary forms, with or without&lt;/span&gt;
&lt;span class="c1"&gt;# modification, are permitted provided that the following conditions are met:&lt;/span&gt;
&lt;span class="c1"&gt;#     * Redistributions of source code must retain the above copyright&lt;/span&gt;
&lt;span class="c1"&gt;#       notice, this list of conditions and the following disclaimer.&lt;/span&gt;
&lt;span class="c1"&gt;#     * Redistributions in binary form must reproduce the above copyright&lt;/span&gt;
&lt;span class="c1"&gt;#       notice, this list of conditions and the following disclaimer in the&lt;/span&gt;
&lt;span class="c1"&gt;#       documentation and/or other materials provided with the distribution.&lt;/span&gt;
&lt;span class="c1"&gt;#     * Neither the name of the &amp;lt;organization&amp;gt; nor the&lt;/span&gt;
&lt;span class="c1"&gt;#       names of its contributors may be used to endorse or promote products&lt;/span&gt;
&lt;span class="c1"&gt;#       derived from this software without specific prior written permission.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; AND&lt;/span&gt;
&lt;span class="c1"&gt;# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;
&lt;span class="c1"&gt;# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&lt;/span&gt;
&lt;span class="c1"&gt;# DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; BE LIABLE FOR ANY&lt;/span&gt;
&lt;span class="c1"&gt;# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES&lt;/span&gt;
&lt;span class="c1"&gt;# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;&lt;/span&gt;
&lt;span class="c1"&gt;# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND&lt;/span&gt;
&lt;span class="c1"&gt;# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT&lt;/span&gt;
&lt;span class="c1"&gt;# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS&lt;/span&gt;
&lt;span class="c1"&gt;# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; -e
&lt;span class="nb"&gt;set&lt;/span&gt; -o pipefail

&lt;span class="nv"&gt;SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.config/2fa

&lt;span class="k"&gt;function&lt;/span&gt; init&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    mkdir -p &lt;span class="nv"&gt;$SOURCE_DIR&lt;/span&gt;
    chmod &lt;span class="m"&gt;0700&lt;/span&gt; &lt;span class="nv"&gt;$SOURCE_DIR&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; ! &lt;span class="nb"&gt;hash&lt;/span&gt; gpg &lt;span class="m"&gt;2&lt;/span&gt;&amp;gt;/dev/null &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Please ensure that GnuPG is installed!&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; ! &lt;span class="nb"&gt;hash&lt;/span&gt; oathtool &lt;span class="m"&gt;2&lt;/span&gt;&amp;gt;/dev/null &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Please ensure that oathtool is installed!&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; add_key&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Adding a new key&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nv"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;What&amp;#39;s the identifier?&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;read&lt;/span&gt; -r identifier
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;What&amp;#39;s the secret?&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;read&lt;/span&gt; -r secret
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; gpg --quiet --symmetric --out &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; get_totp&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;x&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nv"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;What&amp;#39;s the identifier?&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;read&lt;/span&gt; -r identifier
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="nv"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;gpg --quiet &amp;lt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    oathtool --base32 --totp &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$secret&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; list&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    ls -1 &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; help&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Setup a new TOTP account or generate a new TOTP token from an existing account.&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Usage: totp.sh [--add|--list|--totp] [IDENTIFIER]&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;quot;--add     Will ask for an identifier (i.e. &amp;#39;google&amp;#39;, &amp;#39;slack&amp;#39;, ...) and\\n&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;         then for the secret provided by the service provider.&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;quot;--list    Will list all available identifiers.&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;quot;--totp    Will ask for an identifier (i.e. &amp;#39;google&amp;#39;, &amp;#39;slack&amp;#39;, ...) and\\n&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;         then return the TOTP token.&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

init

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    --add&lt;span class="o"&gt;)&lt;/span&gt;
        add_key &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    --list&lt;span class="o"&gt;)&lt;/span&gt;
        list
        &lt;span class="p"&gt;;;&lt;/span&gt;
    --totp&lt;span class="o"&gt;)&lt;/span&gt;
        get_totp &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    *&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;help&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;How do I use the tool? Let&amp;#8217;s say we want to add &lt;span class="caps"&gt;2FA&lt;/span&gt; to our Google account. You
copy the &lt;span class="caps"&gt;2FA&lt;/span&gt; key from Google&amp;#8217;s website and add it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ totp.sh --add google
Adding a new key
What&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;s the secret?
abcd efgh &lt;span class="m"&gt;1234&lt;/span&gt; ijkl mnop &lt;span class="m"&gt;5678&lt;/span&gt; 90qr stuv
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this point you&amp;#8217;ll be asked for a password and confirmation to secure the
secret. Repeat the above with different identifiers for various services you
want.&lt;/p&gt;
&lt;p&gt;You can then list all available services / identifiers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ totp.sh --list
google
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And lastly, you can get the &lt;span class="caps"&gt;TOTP&lt;/span&gt; back:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ totp.sh --totp google
&lt;span class="m"&gt;612027&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="Development"></category><category term="Bash"></category><category term="2FA"></category><category term="Security"></category><category term="TOTP"></category></entry><entry><title>Oh, I Found a Security Issue</title><link href="https://markusholtermann.eu/2017/11/oh-i-found-a-security-issue/" rel="alternate"></link><published>2017-11-22T00:00:00+01:00</published><updated>2017-11-22T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2017-11-22:/2017/11/oh-i-found-a-security-issue/</id><summary type="html">&lt;p class="first last"&gt;An explanation and insight into Django&amp;#8217;s security process and how you
can adopt a similar process for your project.&lt;/p&gt;
</summary><content type="html">&lt;img alt="PyCon CA 2017 logo" class="margin-left align-right" src="/images/pyconca2017/logo.png" /&gt;
&lt;h2&gt;Django&amp;#8217;s Security Process&lt;/h2&gt;
&lt;p&gt;When you&amp;#8217;ve used Django and follow one of our email lists you will likely have
seen an announcement of a Django security release much like this one from April
4th this year:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;Date: Tue, 4 Apr 2017 08:31:25 -0700 (&lt;span class="caps"&gt;PDT&lt;/span&gt;)&lt;/div&gt;
&lt;div class="line"&gt;From: Tim Graham &amp;lt;&amp;#8230;..&amp;#64;gmail.com&amp;gt;&lt;/div&gt;
&lt;div class="line"&gt;To: django-announce &amp;lt;&lt;a class="reference external" href="mailto:django-announce&amp;#64;googlegroups.com"&gt;django-announce&amp;#64;googlegroups.com&lt;/a&gt;&amp;gt;&lt;/div&gt;
&lt;div class="line"&gt;Subject: [django-announce] Django security releases issued: 1.10.7, 1.9.13,
and 1.8.18&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Today the Django team issued 1.10.7, 1.9.13, and 1.8.18 as part of our
security process. These releases address two security issues, and we
encourage all users to upgrade as soon as possible:
&lt;a class="reference external" href="https://www.djangoproject.com/weblog/2017/apr/04/security-releases/"&gt;https://www.djangoproject.com/weblog/2017/apr/04/security-releases/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As a reminder, we ask that potential security issues be reported via private
email to &lt;a class="reference external" href="mailto:security&amp;#64;djangoproject.com"&gt;security&amp;#64;djangoproject.com&lt;/a&gt; and not via Django&amp;#8217;s Trac instance or
the django-developers list. Please see
&lt;a class="reference external" href="https://www.djangoproject.com/security"&gt;https://www.djangoproject.com/security&lt;/a&gt; for further information.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post is how we, the Django team, get to the state where we are able to
send out such an announcement.&lt;/p&gt;
&lt;h3&gt;Reporting the issue&lt;/h3&gt;
&lt;p&gt;The process usually starts with somebody reporting a security issue to the
security team. The preferred way to contact us is &lt;a class="reference external" href="mailto:security&amp;#64;djangoproject.com"&gt;security&amp;#64;djangoproject.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While we can&amp;#8217;t give a guaranteed response time you can expect an acknowledgment
within 24 hours. Please do not hesitate to send a follow-up email if you do not
get a response within 2-3 days. Depending on the severity of the issue maybe
even earlier.&lt;/p&gt;
&lt;p&gt;Apart from that, do not under any circumstances, communicate your findings to
anybody else. The Django security team will take care of the public
announcement later.&lt;/p&gt;
&lt;h3&gt;Assessing the reported issue&lt;/h3&gt;
&lt;p&gt;Once the security team got your email, it will assess the issue. You can expect
to be included in the discussion. This allows you to make sure your reported
issue is addressed and that the security team is not missing the point.&lt;/p&gt;
&lt;h3&gt;Fixing the issue&lt;/h3&gt;
&lt;p&gt;The security team will also ask you if you want to supply a patch or if it
should fix the issue itself.&lt;/p&gt;
&lt;p&gt;Fixing the issue is often not hard. We are allowed to sacrifice backward
compatibility in case of security issues. On top of that, history has shown
that many patches are only a few lines of code checking just &lt;span class="caps"&gt;ONE&lt;/span&gt; &lt;span class="caps"&gt;MORE&lt;/span&gt; &lt;span class="caps"&gt;THING&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Confirming the fix&lt;/h3&gt;
&lt;p&gt;At some point, there is a patch available.&lt;/p&gt;
&lt;p&gt;If you wrote the patch the security team will review it.&lt;/p&gt;
&lt;p&gt;If somebody from the security team wrote the patch, we kindly ask you to review
the patch to ensure it actually fixes the issue you reported and we don&amp;#8217;t miss
something.&lt;/p&gt;
&lt;p&gt;This is a very important part of the process, even with multiple people
confirming that the patch works, there is still the possibility of missing
something.&lt;/p&gt;
&lt;h3&gt;Pre-notification&lt;/h3&gt;
&lt;p&gt;Once the patch is &amp;#8220;ready&amp;#8221;, to be committed to the release branches, we send out
pre-notification emails to some people and organizations. We will not make this
list public. It mostly consists of distributors and organizations with lots of
personal data.&lt;/p&gt;
&lt;p&gt;Aside from the detailed, private notification, we announce the upcoming
security release on the django-announce mailing list with a time frame during
which the release will be made available.&lt;/p&gt;
&lt;h3&gt;Release &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Announcement&lt;/h3&gt;
&lt;p&gt;About a week after the pre-notification there&amp;#8217;s the security release.&lt;/p&gt;
&lt;p&gt;At that point, we will commit the security patches publicly. From that point on
we consider the security issue to be disclosed publicly.&lt;/p&gt;
&lt;p&gt;After that, somebody from the release team will cut the corresponding Django
releases.&lt;/p&gt;
&lt;p&gt;We will then publish a blog post with some details on the security issue. We
will not publish exploits.&lt;/p&gt;
&lt;p&gt;We will publish Common Vulnerability and Exposure numbers — or CVEs — so that
you can check against your downstream vendor&amp;#8217;s &lt;span class="caps"&gt;CVE&lt;/span&gt; database to see if they
patched the vulnerability.&lt;/p&gt;
&lt;p&gt;Furthermore, we will announce the releases to the django-announce and &lt;a class="reference external" href="http://www.openwall.com/lists/oss-security/"&gt;oss-
security OpenWall mailing list&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Implementing these steps yourself&lt;/h2&gt;
&lt;p&gt;As you can see, there&amp;#8217;s a lot of process on handling security issues properly.
At least we think we handle them properly.&lt;/p&gt;
&lt;p&gt;How can you apply this to your library and make it more secure?&lt;/p&gt;
&lt;h3&gt;Setup reporting channel&lt;/h3&gt;
&lt;p&gt;First and foremost, communicate a way people can securely reach out to you to
report a security issue.&lt;/p&gt;
&lt;p&gt;A sentence like &amp;#8220;If you think you found a security issue please get in touch
via &lt;span class="caps"&gt;FOO&lt;/span&gt;&amp;#8221; goes a long way. And &lt;span class="caps"&gt;PLEASE&lt;/span&gt;, including the actual email address there
goes a long way.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t let somebody who wants to help you find a communication channel
themselves. Tell them that even a suspicion of a security issue is worth
reporting privately. Even if it turns out to not be an issue.&lt;/p&gt;
&lt;h3&gt;Monitor reporting channel&lt;/h3&gt;
&lt;p&gt;I hope it goes without saying that you have to monitor whatever communication
channel you choose.&lt;/p&gt;
&lt;p&gt;If you don&amp;#8217;t have the time to monitor 24/7, which I believe nobody in the room
has, add something like &amp;#8220;you should receive an acknowledgment within 7 days&amp;#8221; or
whatever is reasonable &lt;span class="caps"&gt;FOR&lt;/span&gt; &lt;span class="caps"&gt;YOU&lt;/span&gt;! This gives the reporter the information that
they may need to wait for a short time.&lt;/p&gt;
&lt;p&gt;Just because &lt;span class="caps"&gt;YOU&lt;/span&gt; develop an open source library in your free time doesn&amp;#8217;t mean
that you need to be around fixing it 24/7. Have a life besides it, please.&lt;/p&gt;
&lt;h3&gt;Fix the issue&lt;/h3&gt;
&lt;p&gt;Find a way to fix the bug. Communicate with the reporter. Once you think you
are good with you have, ask the reporter to confirm your patch. &lt;span class="caps"&gt;BEFORE&lt;/span&gt; you make
it public!&lt;/p&gt;
&lt;h3&gt;Release &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Announce&lt;/h3&gt;
&lt;p&gt;Commit and release a new version of your library or program. Inform people
about the release. Communicate where you are going to release such information
at a prominent place so people can subscribe to updates.&lt;/p&gt;
&lt;p&gt;My suggestion is to generally report to the oss-security OpenWall list.&lt;/p&gt;
&lt;h3&gt;Learn from it&lt;/h3&gt;
&lt;p&gt;For your own sake, accept the fact that you had a security issue in your
software and don&amp;#8217;t stress out over it. We&amp;#8217;re all humans. We all make mistakes!
Learn from it and try to not make this mistake again.&lt;/p&gt;
&lt;p&gt;For what it&amp;#8217;s worth, Django received over 60 CVEs since its release. And
there&amp;#8217;s a whole bunch of people reviewing code.&lt;/p&gt;
&lt;h2&gt;&lt;span class="caps"&gt;OWASP&lt;/span&gt; Top 10&lt;/h2&gt;
&lt;p&gt;One thing I want to mention here is the &lt;a class="reference external" href="https://www.owasp.org/"&gt;Open Web Application Security Project&lt;/a&gt; or &lt;span class="caps"&gt;OWASP&lt;/span&gt;. They provide a Top 10 of security issue
classifications that people should care about. It&amp;#8217;s often considered a
&amp;#8220;standard&amp;#8221; but there are some loud voices that claim &lt;span class="caps"&gt;OWASP&lt;/span&gt; is not focusing on
the real issues but tries to tackle them from a higher or business level. Have
a look at it and decide for yourself.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The PyCon Canada &lt;a class="reference external" href="https://github.com/pyconca/2017-web/blob/28bb39347b1e044feb930aae6de6be8095973233/pyconca2017/static/toolkit/images/illustrations/illustrations.svg"&gt;illustration&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/pyconca/2017-web/blob/28bb39347b1e044feb930aae6de6be8095973233/pyconca2017/static/toolkit/images/logo-full.svg"&gt;logo&lt;/a&gt; are licensend under the &lt;span class="caps"&gt;MIT&lt;/span&gt; license.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/oh-i-found-a-security-issue-pycon-ca-2017"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Misc"></category><category term="Django"></category><category term="PyCon"></category><category term="Security"></category><category term="Python"></category><category term="Talk"></category></entry><entry><title>Thoughts About Normal and Abnormal Data</title><link href="https://markusholtermann.eu/2017/10/thoughts-about-normal-and-abnormal-data/" rel="alternate"></link><published>2017-10-27T00:00:00+02:00</published><updated>2017-10-27T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2017-10-27:/2017/10/thoughts-about-normal-and-abnormal-data/</id><summary type="html">&lt;p class="first last"&gt;A lot of data lives in relational databases. And there are relations
between records in these databases. Relations that might be normal or
abnormal.&lt;/p&gt;
</summary><content type="html">&lt;img alt="PyCon UK 2017 logo" class="margin-left align-right" src="/images/pyconuk2017/logo.jpg" /&gt;
&lt;p&gt;A lot of our data lives in databases. A lot of those databases are relational
databases. They are called relational due to the way how objects refer to each
other. But just because having references between certain records is &amp;#8220;normal&amp;#8221;
and &amp;#8220;what you should do&amp;#8221; it doesn&amp;#8217;t mean it&amp;#8217;s the best choice.&lt;/p&gt;
&lt;p&gt;This post is not about files. Neither in a file room nor your spreadsheet or
&lt;span class="caps"&gt;PDF&lt;/span&gt; document. Everyone has their own system of how they store their files. And
I think that&amp;#8217;s a good thing.&lt;/p&gt;
&lt;p&gt;This post is also not about document stores or document databases like MongoDB
or CouchDB. In my opinion all these data stores have their reason to exist.
It&amp;#8217;s just not something I am looking into here.&lt;/p&gt;
&lt;p&gt;What I am going to cover are relational databases, such as PostgreSQL, MySQL
and a whole lot more. The key here lies on &lt;em&gt;relational&lt;/em&gt; databases.&lt;/p&gt;
&lt;p&gt;In order to understand what normal or abnormal data is, I need to talk about
some database theory.&lt;/p&gt;
&lt;h2&gt;Database Theory&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s say we want to store a few person names, which planets they are from and
what gender they identify with.&lt;/p&gt;
&lt;p&gt;If you were to put that information in a database table this might very well
look a bit like this:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="19%" /&gt;
&lt;col width="58%" /&gt;
&lt;col width="23%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Home planet&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Padmé&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Luke&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Alderaan, Naboo&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There is a problem with this format, though. You need to store two planet names
in a single cell. This is certainly possible but will eventually lead to
problems. For example filtering for all people from Naboo can get tricky. If
you are using PostgreSQL you can put that into an array field that can get
around a few of these issues but not all.&lt;/p&gt;
&lt;h2&gt;First Normal Form&lt;/h2&gt;
&lt;p&gt;This is the time where in relational database theory the so called &lt;em&gt;First
Normal Form&lt;/em&gt; or &lt;em&gt;&lt;span class="caps"&gt;1NF&lt;/span&gt;&lt;/em&gt; is introduced.&lt;/p&gt;
&lt;p&gt;First mentioned by Edgar Codd in 1971, there are a few criteria a table needs
to fulfill for it to be considered in First Normal Form. Firstly, each cell
must have atomic or indivisible entities. Secondly, each row needs to be
uniquely identifiable.&lt;/p&gt;
&lt;p&gt;When we apply these two rules, our table can look like this:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="27%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="37%" /&gt;
&lt;col width="20%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Home planet&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Padmé&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Luke&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We introduced a PersonID that we will start to use to reference a person. We
also changed the combined &amp;#8220;Alderaan, Naboo&amp;#8221; value into two distinct rows.
However, now the PersonID itself is not uniquely identifying anymore. As such,
the key to identify a person now is a combination of PersonID and Home Planet.&lt;/p&gt;
&lt;p&gt;This is all great and we&amp;#8217;re happy now, but we can run into a problem over time
while using our data. This table suffers from Update Anomalies. Let&amp;#8217;s look at a
subset at the previous table:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="25%" /&gt;
&lt;col width="16%" /&gt;
&lt;col width="34%" /&gt;
&lt;col width="25%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Home planet&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Male&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If Leia were to change her gender and would identify herself as male, we would
need to update every row in the table with the new gender. If we don&amp;#8217;t update
all rows but instead only update some, we end up with inconsistent data. And
that is something you certainly don&amp;#8217;t want to have happen to your production
data.&lt;/p&gt;
&lt;h2&gt;Second Normal Form&lt;/h2&gt;
&lt;p&gt;To remedy this issue there exists a &lt;em&gt;Second Normal Form&lt;/em&gt; or &lt;em&gt;&lt;span class="caps"&gt;2NF&lt;/span&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The Second Normal Form was defined by Edgar Codd in 1971 as well. In addition
to the criteria from the First Normal Form it requires that &lt;em&gt;all non-key
attributes are fully functional dependent on the primary key&lt;/em&gt;. Now, what does
that even mean? Let&amp;#8217;s look at the table again:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="27%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="37%" /&gt;
&lt;col width="20%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Home planet&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Padmé&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Luke&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The primary key is the combination of PersonID and Home Planet. Because that&amp;#8217;s what uniquely identifies are row.&lt;/p&gt;
&lt;p&gt;A person&amp;#8217;s name as well as their gender are each non-key attributes. They both
are also functional dependent on the primary key. But don&amp;#8217;t depend on the &lt;span class="caps"&gt;FULL&lt;/span&gt;
primary key, but only part thereof. Name and gender only depend on the
PersonID, so to say. The Home Planet doesn&amp;#8217;t influence either. As such, this
table violate the Second Normal Form.&lt;/p&gt;
&lt;p&gt;We can solve this violation, though:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="26%" /&gt;
&lt;col width="32%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Padmé&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Luke&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="58%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Planet name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If we move the Planet Name into its own table and only reference the PersonID,
we comply with the Second Normal Form. So far so good.&lt;/p&gt;
&lt;p&gt;But now we want to extend our table of planet names. We want to add the planet
Dagobah to our list. However, trying to do so will give us some trouble if we
don&amp;#8217;t have at least one person in our person table that is considering that
planet their home planet. If we were to insert Dagobah into our planet table we
couldn&amp;#8217;t. There&amp;#8217;s no person in our person table that would have Dagobah as
their home planet:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="58%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Planet name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;???&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dagobah&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;While this might not be an issue for fixed-data scenarios, it certainly is an
issue in most situations these days.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s also another problem you might have already thought of: when we have
trouble inserting data, we might as well have issues deleting it. And you&amp;#8217;re
right there.&lt;/p&gt;
&lt;p&gt;Deletion Anomalies can happen as well. If we were to delete Luke from our
persons table, then Tatooine wouldn&amp;#8217;t have a person anymore that it belongs to.
We&amp;#8217;d thus need to delete that planet from the planet table as well:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="26%" /&gt;
&lt;col width="32%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Padmé&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="58%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Planet name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;???&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That&amp;#8217;s kind of unfortunate, isn&amp;#8217;t it? Let&amp;#8217;s remedy this issue. You may already
guess, when there is a First and Second Normal Form there probably is a &lt;em&gt;Third
Normal Form&lt;/em&gt; as well.&lt;/p&gt;
&lt;h2&gt;Third Normal Form&lt;/h2&gt;
&lt;p&gt;And you&amp;#8217;re right about that! Like the previous two, the &lt;em&gt;Third Normal Form&lt;/em&gt; or
&lt;em&gt;&lt;span class="caps"&gt;3NF&lt;/span&gt;&lt;/em&gt; was defined by Edgar Codd in 1971. Simply put, in addition to the
criteria for from the First and Second Normal Forms, it requires that all
non-key attributes are dependent on the primary key, and only the primary key.&lt;/p&gt;
&lt;p&gt;Looking at our table schema now, we have the person table with the primary key
PersonID. Name and gender only depend on the PersonID. We could have more
persons with the same name and different genders:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="26%" /&gt;
&lt;col width="32%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Name&lt;/th&gt;
&lt;th class="head"&gt;Gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Padmé&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Luke&lt;/td&gt;
&lt;td&gt;Male&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Leia&lt;/td&gt;
&lt;td&gt;Female&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Likewise for the planets, although multiple planets with the same name are
somewhat unrealistic. The name and the amount of surface water only depend on
the PlanetID primary key:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="58%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;Planet name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;???&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tatooine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Alderaan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Naboo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;New to the party here is the table combining PersonID and PlanetID. Those two
fields also represent the table&amp;#8217;s primary key. The table represents the m-to-n
relationships between persons and planets. Thus preventing insert and deletion
anomalies:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="50%" /&gt;
&lt;col width="50%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;PersonID&lt;/th&gt;
&lt;th class="head"&gt;PlanetID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="first admonition-title"&gt;Tip&lt;/p&gt;
&lt;p class="last"&gt;Database normalization is great!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;A normalized database can solves the issues of insert, update and deletion
anomalies I pointed out. But should you always normalize your database? Is a
fully normalized database always a good idea?&lt;/p&gt;
&lt;h2&gt;Another Example&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s have a look at another example, a wiki. Imagine a simple wiki with pages
where each page can have an arbitrary number of revisions. From a relational
database perspective this could look a bit like this:&lt;/p&gt;
&lt;h3&gt;Page&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;PageID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Slug&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Revision&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;RevisionID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;PageID&lt;/li&gt;
&lt;li&gt;Text&lt;/li&gt;
&lt;li&gt;Date&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the one hand side we have a Page object that identifies a single page. A
page has a unique &lt;span class="caps"&gt;ID&lt;/span&gt; that we use internally to refer to it. A page also has a
name attribute we use for displaying. And there is a slug attribute that also
uniquely identifies a page object, very much like the PageID, but it is &amp;#8220;human
readable&amp;#8221; and may change together with the name.&lt;/p&gt;
&lt;p&gt;On the other side there is a Revision object. That object is used to store
different versions of the text of a wiki page. The date attribute identifies
when the revision was created. As such, the revision object with the newest
date for a page represents the last revision.&lt;/p&gt;
&lt;p&gt;Given that schema I would want 2 tasks to be solved:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Task 1: Fetch a single page and its current revision&lt;/li&gt;
&lt;li&gt;Task 2: Fetch all page titles and the date of their current revision&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As the first task I want to fetch a single page and the corresponding last or
current revision. This is a quite common scenario. A user navigates to your
wiki and you want to show the recent version of a particular page. How do you
do that given the database structure I just showed?&lt;/p&gt;
&lt;p&gt;The second task is somewhat less common but still useful; fetch all page titles
and the date of their current or last revision. This could happen when a user
opens your wiki&amp;#8217;s sitemap or your index and you want to list all pages with
their last revision data.&lt;/p&gt;
&lt;p&gt;Well, for the first task, this is probably the simplest &lt;span class="caps"&gt;SQL&lt;/span&gt; query you could
find that works across different databases. Using common table expressions or
window functions if you are on PostgreSQL could be beneficial. But this is a
pure &lt;span class="caps"&gt;SQL&lt;/span&gt; standard:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;some-slug&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You filter for the slug of the page, join your page and revision tables on the
page id, sort by the revision date in descending order, and select the top 1
result.&lt;/p&gt;
&lt;p&gt;Similarly for the second task we are fetching all pages with their most recent
revision date. Adhering to what pretty much all relational databases
understand:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_revs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;
  &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;last_revs&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;last_revs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We&amp;#8217;re selecting the maximum revision date and corresponding page id for each
page from the revisions table, join the result with the pages on the page id,
and return the page&amp;#8217;s name and last revision date.&lt;/p&gt;
&lt;p&gt;Now with those two queries, let&amp;#8217;s look at some numbers and statistics.&lt;/p&gt;
&lt;h2&gt;Benchmarking&lt;/h2&gt;
&lt;p&gt;These scientifically absolutely unproven tests ran on an Intel i7, &lt;span class="caps"&gt;8GB&lt;/span&gt; &lt;span class="caps"&gt;RAM&lt;/span&gt; and
PostgreSQL 9.6.5. The database contained 10 thousand pages and 6 million
revisions in total. Randomly created with an overall average distribution.&lt;/p&gt;
&lt;p&gt;For my &amp;#8220;benchmark&amp;#8221; I ran the first query with 10 concurrent connections. Each
connection fetched a fixed random subset of 1000 pages. Each page was fetched
10 times in a row. That comes down to a total of 100 thousand queries.&lt;/p&gt;
&lt;p&gt;The second task I decided to just run the same query 10 times to get an idea of
the timing. The second query is also not dependent on a particular page but on
the full table.&lt;/p&gt;
&lt;p&gt;For the first task, on average, the first call for a page took almost 20
milliseconds, whereas the subsequent calls were about 2.5 to 3 milliseconds.
That&amp;#8217;s easily explainable by cache hits for subsequent calls.&lt;/p&gt;
&lt;p&gt;For the second task the query run time varied from about 3.5 to 7 seconds.
There also wasn&amp;#8217;t any useful caching happening. The cache misses are certainly
something one can look at in details, but that&amp;#8217;s beyond the talk and this post.&lt;/p&gt;
&lt;p&gt;What I instead want to do is, restructure and denormalize the database
structure a bit.&lt;/p&gt;
&lt;h2&gt;Denormalizing our Database&lt;/h2&gt;
&lt;p&gt;The only change compared to the previous schema is the added LastRevision
column on a page which references, as you might have guessed, to the currently
active revision for a page.&lt;/p&gt;
&lt;h3&gt;Page&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;PageID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Slug&lt;/li&gt;
&lt;li&gt;&lt;em&gt;LastRevision&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Revision&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;RevisionID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;PageID&lt;/li&gt;
&lt;li&gt;Text&lt;/li&gt;
&lt;li&gt;Date&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously, our &lt;span class="caps"&gt;SQL&lt;/span&gt; will have to change.&lt;/p&gt;
&lt;p&gt;Instead of doing ordering in descending order by date and then limiting the
result set to a single record, we can now directly access to last revision:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_revision_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;some-slug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the second task we don&amp;#8217;t need any subquery anymore which will make the
query significantly more efficient:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_revision_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;revision_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The picture the numbers for the first task draw looks quite similar. Except
that the query time for the first call went from 20 milliseconds to just above
0.3 milliseconds. That is about two orders of magnitude faster for the first
call.&lt;/p&gt;
&lt;p&gt;The picture for the second task, though, is something I really like. Not only
dropped the absolute query time for all calls, but subsequent calls of the same
query heavily hit the cache.&lt;/p&gt;
&lt;p&gt;While the average query time was around 6 seconds before, using the
denormalized schema the query run time is about a 10th of that for the first
call. Subsequent queries are about 200 times faster than the original average.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I guess what I want to show, normalizing your database is a good idea to
prevent anomalies. It&amp;#8217;s also a good idea to prevent data duplication and to
keep your data consistent.&lt;/p&gt;
&lt;p&gt;But what I also want to show you, there&amp;#8217;s a time at which you should rethink
your database design and may denormalize your database a bit. The important
part is that you don&amp;#8217;t start with that right in the beginning when you setup
your project. You will not know how your project evolves and denormalization
adds another layer of complexity. When you know your project, and when your
database reaches a certain size, I suggest you look at the single bottleneck at
a time.&lt;/p&gt;
&lt;p&gt;Check what&amp;#8217;s important for your users. And start there to optimize. But only go
as far with denormalization as you feel comfortable. Because every layer of
denormalization you add, needs to be maintained.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/thoughts-about-normal-and-abnormal-data-pycon-uk-2017"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Misc"></category><category term="Database"></category><category term="Python"></category><category term="Talk"></category></entry><entry><title>Security and Ease-of-Use With Django Templates</title><link href="https://markusholtermann.eu/2017/06/security-and-ease-of-use-with-django-templates/" rel="alternate"></link><published>2017-06-24T00:00:00+02:00</published><updated>2017-06-24T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2017-06-24:/2017/06/security-and-ease-of-use-with-django-templates/</id><summary type="html">&lt;p class="first last"&gt;Usability is key in modern web applications. But we live in a
dangerous world where websites get hacked. Thoughts on pros &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; cons.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I engaged in a bit of a Twitter discussion that started when &lt;a class="reference external" href="https://twitter.com/legostormtroopr"&gt;Samuel Spencer&lt;/a&gt; showed &lt;a class="reference external" href="https://twitter.com/legostormtroopr/status/877810010784387072"&gt;a Django package to modify page
templates&lt;/a&gt;
through the Django admin.&lt;/p&gt;
&lt;p&gt;This blog post &lt;em&gt;is not&lt;/em&gt; intended to blame him for developing the package. This
blog post &lt;em&gt;is not&lt;/em&gt; intended to discourage anybody from using that package. This
blog post &lt;em&gt;is&lt;/em&gt; intended to educate about possibly risks that arise from using
that package or, in general, blindly trust and run user supplied content which
is something this package does &lt;em&gt;to some degree&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;: We live in a dangerous world. Security is hard. Don&amp;#8217;t trust anything.&lt;/p&gt;
&lt;p&gt;Ok. Let&amp;#8217;s me a bit more serious and specific. Let&amp;#8217;s weigh the pros and cons.
Let&amp;#8217;s see what&amp;#8217;s actually at stake.&lt;/p&gt;
&lt;h2&gt;The Deployment&lt;/h2&gt;
&lt;p&gt;Deploying Django applications is more complicated than your typical WordPress
site. At least when you get started with Django. There are dozens of articles,
conference talks, and mailing list discussions on that topic. Google it if you
want specifics. In essence, deploying a Django project is not simply done by
copying some files to a document root on a web server as it is common with &lt;span class="caps"&gt;PHP&lt;/span&gt;.
You&amp;#8217;ll probably use an application server like gunicorn and uwsgi. Or you
figured out how to use mod_wsgi in the Apache &lt;span class="caps"&gt;HTTP&lt;/span&gt; server which then works
similarly to &lt;span class="caps"&gt;PHP&lt;/span&gt; yet completely different. But when you managed to get all
changes to your server, you still need to tell the application server to reload
and restart your application. And this is &lt;em&gt;a good thing&lt;/em&gt; ™. You&amp;#8217;ll see why that
is later.&lt;/p&gt;
&lt;h2&gt;When You Screw Up&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s assume you have this magic way to modify the &lt;span class="caps"&gt;HTML&lt;/span&gt; template files for your
Django project in the browser. That&amp;#8217;s great, as you can now make some quick fix
online without the need to commit code and trigger your deployment pipeline. So
it&amp;#8217;s perfectly suitable to perform those changes from your smartphone given a
&lt;a class="reference external" href="https://code.djangoproject.com/ticket/26818"&gt;responsive Django admin&lt;/a&gt;.
Wrong!&lt;/p&gt;
&lt;p&gt;Guess what happens when you modify your &lt;tt class="docutils literal"&gt;base.html&lt;/tt&gt; file and have a syntax
error in there. The potentially harmless case is a broken &lt;tt class="docutils literal"&gt;&amp;lt;a&amp;gt;&lt;/tt&gt; tag, a messed
up a list definition or whatnot. Or you accidentally remove the keywords from
the &lt;tt class="docutils literal"&gt;&amp;lt;head&amp;gt;&lt;/tt&gt; section so your &lt;span class="caps"&gt;SEO&lt;/span&gt; ranking goes downhill. None of these things
actually are a real issue for the matter of this post. Your site will still be
available. Users can still access your page and read its content. Modern
browsers will cope with all kinds of broken &lt;span class="caps"&gt;HTML&lt;/span&gt; structures. So, all good,
right? Wrong!&lt;/p&gt;
&lt;p&gt;What happens if you accidentally removed some escaping from some Django
template language variables, such as &lt;tt class="docutils literal"&gt;escapejs&lt;/tt&gt; inside a JavaScript block?
Well, the good thing, your site will still &amp;#8220;work&amp;#8221; and is not going to throw a
&lt;a class="reference external" href="https://httpstatuses.com/500"&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; 500 Internal Server Error&lt;/a&gt;. Probably, at
least. Lucky you. But maybe not. This is one of those subtle issues you will
only know about once somebody exploited an issue with unescaped JavaScript.
Which is now possible due to to missing &lt;tt class="docutils literal"&gt;escapejs&lt;/tt&gt;, remember?&lt;/p&gt;
&lt;p&gt;I think I&amp;#8217;d prefer to completely screw up my Django template syntax. At least
the issue will be brutal when the entire page won&amp;#8217;t load. You should notice
that, right? Depends! Only if you have proper monitoring or error reporting.&lt;/p&gt;
&lt;p&gt;But that&amp;#8217;s not all. That&amp;#8217;s just the start. Depending on which template file you
just broke, you might as as well broke your Django admin template.
Congratulations, you&amp;#8217;re now forced to figure out a way to revert that. Likely
without &lt;em&gt;any&lt;/em&gt; form of web &lt;span class="caps"&gt;UI&lt;/span&gt;. Oh, and of course, you&amp;#8217;re on your phone in the
middle of nowhere with crappy internet such that an &lt;span class="caps"&gt;SSH&lt;/span&gt; connection cracks down
every 5 minutes. Sucks for you, I guess.&lt;/p&gt;
&lt;h2&gt;Something Else Is Insecure&lt;/h2&gt;
&lt;p&gt;Given the example from the introduction &amp;#8212; a view in the Django admin that lets
you modify the template files &amp;#8212; what happens if there&amp;#8217;s a privilege escalation
in the Django admin that lets arbitrary users modify those templates. We&amp;#8217;ll,
that unfortunate! (On that note, if you encounter or &lt;em&gt;think&lt;/em&gt; you encountered a
security issue in Django, please reach out to &lt;a class="reference external" href="mailto:security&amp;#64;djangoproject.com"&gt;security&amp;#64;djangoproject.com&lt;/a&gt; and
&lt;strong&gt;do not&lt;/strong&gt; report the issue through the issue tracker).&lt;/p&gt;
&lt;p&gt;You&amp;#8217;re pretty much out of luck here until you there&amp;#8217;s a patched Django release
you can use. But in the meantime, &lt;span class="strike"&gt;users&lt;/span&gt; (alright, let&amp;#8217;s start calling
&lt;em&gt;those&lt;/em&gt; users attackers now) attackers will be able to exploit the privilege
escalation and have all kinds of fun. Starting with one of those &amp;#8220;you&amp;#8217;ve been
hacked&amp;#8221; memes, over a simple &amp;#8220;breaking the page&amp;#8221; by messing with the Django
template language syntax over sneaking in some evil JavaScript that performs
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cross-site_scripting"&gt;&lt;span class="caps"&gt;XSS&lt;/span&gt; attacks&lt;/a&gt; against you
and your users. Of course nobody is going to notice the latter. Thus you&amp;#8217;re
leaking more and more data to the attackers.&lt;/p&gt;
&lt;p&gt;Now, all previous issues assume an attacker only touches the &lt;span class="caps"&gt;HTML&lt;/span&gt; template
files. While that is bad enough, slightly more subtle changes could be
introduced by exploiting an issue in the online editor that would allow an
attacker to modify arbitrary files. Like, the JavaScript files shipped to a
user. But again, this would manifest itself instantly. The &amp;#8220;fun&amp;#8221; part begins
when an attack would be able to view (and potentially write) Python files
belonging to your project. I mean, what could possibly go wrong when &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECRET_KEY"&gt;Django&amp;#8217;s
SECRET_KEY or database credentials are leaked&lt;/a&gt;,
right? Apart from being able to connect to your database they could also change
the Python code and essentially do whatever they want. Well, almost. Because
most (all?) Python web application servers need to be told to reload the web
application and thus actually see those changes. In order to do that an
attacker would thus need to restart the application server. And that&amp;#8217;s not
&lt;em&gt;that&lt;/em&gt; easily possible unless you can already run arbitrary code. On the other
hand with &lt;span class="caps"&gt;PHP&lt;/span&gt;, changes are reflected instantly (at least as far as I remember.
I&amp;#8217;ve not done any &lt;span class="caps"&gt;PHP&lt;/span&gt; development for a while).&lt;/p&gt;
&lt;h2&gt;When To Use Such Features&lt;/h2&gt;
&lt;p&gt;Well, if you want to err on the side of caution: never.&lt;/p&gt;
&lt;p&gt;If you do use those tools, make sure you&amp;#8217;ve &lt;a class="reference external" href="https://www.owasp.org/index.php/Application_Threat_Modeling"&gt;come up&lt;/a&gt; with a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Threat_model"&gt;threat
model&lt;/a&gt; and risk model. If they
say the usability and user experience outweighs the risk and threat, sure, go
ahead. If they say it&amp;#8217;s too risky, well, don&amp;#8217;t use such things.&lt;/p&gt;
&lt;p&gt;On a higher level you can probably reduce the decision making to this: Is your
site on the internet, don&amp;#8217;t use such features. Is it company internal, e.g.
behind a &lt;span class="caps"&gt;VPN&lt;/span&gt; and only a limited set of users have access to it, you&amp;#8217;re probably
fine. A company usually has bigger problems when there&amp;#8217;s a rough admin that
starts XSSing its colleagues. Is it your own tool you use for yourself only and
nobody else has access to it, sure, go ahead, use such tools if they make your
life easier.&lt;/p&gt;
&lt;p&gt;In the end it comes down to the &lt;a class="reference external" href="https://blog.infosanity.co.uk/2010/06/12/infosec-triads-securityfunctionalityease-of-use/"&gt;Security, Functionality and Ease-of-Use triad&lt;/a&gt; where you can only ever actually achieve 2
of them.&lt;/p&gt;
&lt;h2&gt;Closing Remarks&lt;/h2&gt;
&lt;p&gt;These are some thoughts I had on the initially linked tweet. I wanted to write
them down to not forget about them. There’s probably a lot more to consider and
at stake that I didn’t mention. I’m more than welcome to continue the
discussion on this topic in said Twitter thread.&lt;/p&gt;
</content><category term="Development"></category><category term="Django"></category><category term="Security"></category></entry><entry><title>To Index Or Not, That’s Not The Questions</title><link href="https://markusholtermann.eu/2017/04/to-index-or-not-is-not-the-question/" rel="alternate"></link><published>2017-04-05T00:00:00+02:00</published><updated>2017-04-05T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2017-04-05:/2017/04/to-index-or-not-is-not-the-question/</id><summary type="html">&lt;p class="first last"&gt;Database indexes are useful. But how do they work? And how does one
use them in Django?&lt;/p&gt;
</summary><content type="html">&lt;img alt="DjangoCon Europe 2017 -- Florence" class="margin-left align-right" src="/images/djangoconeu2017/logo.png" /&gt;
&lt;h2&gt;Some Background Theory&lt;/h2&gt;
&lt;p&gt;As databases are used to store more and more information every day, these are
also a key component in every Django project. Thus it&amp;#8217;s important to understand
how they work.&lt;/p&gt;
&lt;p&gt;Of course I can&amp;#8217;t explain all the details about all the different databases you
can use with Django. Not only, because I don&amp;#8217;t know all of that, but also
because that would make for a hell of a talk. Or probably an entire conference.&lt;/p&gt;
&lt;p&gt;The only thing about I want to say about the theoretical background on
databases is, there&amp;#8217;s something called &amp;#8220;relational algebra&amp;#8221;. With that &lt;em&gt;every&lt;/em&gt;
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt;&lt;/tt&gt; statement you could possibly come up with can be expressed. It&amp;#8217;s
mathematically proven.&lt;/p&gt;
&lt;h2&gt;How Database Lookups Work&lt;/h2&gt;
&lt;p&gt;Instead, let&amp;#8217;s start with how lookups in databases work. Because that is what
we do most the time.&lt;/p&gt;
&lt;p&gt;Assume we have this database table of names of people and their corresponding
age when they first started programming.&lt;/p&gt;
&lt;p&gt;Now we want to select every persons that started at age 19.&lt;/p&gt;
&lt;img alt="Initial table" src="/images/djangoconeu2017/slide05.png" /&gt;
&lt;p&gt;We can express that with a &lt;span class="caps"&gt;SQL&lt;/span&gt; query:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, how can we find each person matching that query?&lt;/p&gt;
&lt;h3&gt;Table Scan Lookup&lt;/h3&gt;
&lt;p&gt;Well, that&amp;#8217;s fairly easy. We just look at each row in the table, check if the
condition applies, and return the row if it does.&lt;/p&gt;
&lt;p&gt;This is called a &amp;#8220;Full Table Scan&amp;#8221;&lt;/p&gt;
&lt;p&gt;So far so good. We have 7 rows here. Thus we look at 7 rows. That&amp;#8217;s a small
number of rows, so the query is rather fast.&lt;/p&gt;
&lt;img alt="Full table scan" src="/images/djangoconeu2017/slide07.png" /&gt;
&lt;p&gt;But imagine you have like 100 thousand, 100 million, 100 billion or even more
rows. Iterating over every single row can be time consuming. And that&amp;#8217;s not
something we want or something we can afford. We want something that provides
guaranteed timing to find a particular row. Independent of the number of rows.&lt;/p&gt;
&lt;p&gt;This is where indexes join the party.&lt;/p&gt;
&lt;h3&gt;What Are Indexes?&lt;/h3&gt;
&lt;p&gt;Indexes give fast access to a single (or multiple) items without much speed
reduction for an increasing amount of data. This is also called &amp;#8220;Random
Access&amp;#8221;. You will see in a bit why it&amp;#8217;s called that way. But first let us look
into the most common index types in modern database systems.&lt;/p&gt;
&lt;h3&gt;B-Trees / B+ Trees&lt;/h3&gt;
&lt;p&gt;The most common indexes these days are B-Tree indexes. Or more precisely, B+
Tree indexes.&lt;/p&gt;
&lt;p&gt;They are either named after one of their inventors, Rudolf Bayer, or because
they are self-balancing. That&amp;#8217;s not really clear but also doesn&amp;#8217;t really
matter. Self-balancing means that the trees have certain timing guarantees: the
most interesting for B-Trees it doesn&amp;#8217;t matter how big the index is, for any
index of the same size the timing is going to be the same. You&amp;#8217;ll see that in a
bit as well.&lt;/p&gt;
&lt;p&gt;As with every tree (both in Computer Science and in nature) you start with a
root. The difference between Computer Science and Nature is, in Computer
Science we put the root of a tree at the top.&lt;/p&gt;
&lt;p&gt;Whatever. Here&amp;#8217;s the root note of a B+ Tree of grade 3.&lt;/p&gt;
&lt;img alt="B+ Tree root node" src="/images/djangoconeu2017/slide10.png" /&gt;
&lt;p&gt;Grade 3 means, this node can have 3 keys. That&amp;#8217;s what the 3 boxes at the top of
that node are for. The 4 boxes underneath hold pointers to another node or a
row in the database table.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#8217;s say you have the keys 11 and 37 in this node and you don&amp;#8217;t have a
3rd key.&lt;/p&gt;
&lt;img alt="B+ Tree filled root node" src="/images/djangoconeu2017/slide11.png" /&gt;
&lt;p&gt;Then the leftmost pointer would point to a node with keys smaller than 11. The
second pointer would point to a node with keys greater or equal than 11 and
smaller than 37. The third pointer would point to a node with keys greater or
equal than 37.&lt;/p&gt;
&lt;p&gt;Having an index on the age column from the table I showed in the beginning,
this could then look like this.&lt;/p&gt;
&lt;img alt="B+ Tree filled leaf nodes" src="/images/djangoconeu2017/slide12.png" /&gt;
&lt;p&gt;The magic now is what happens with the pointers in this second row of nodes.
Each of them points to a single row in the table that has the particular key,
which is the age in our example.&lt;/p&gt;
&lt;img alt="B+ Tree leaf node pointers" src="/images/djangoconeu2017/slide13.png" /&gt;
&lt;p&gt;But it&amp;#8217;s not just this.&lt;/p&gt;
&lt;img alt="B+ Tree leaf node pointers to next node" src="/images/djangoconeu2017/slide14.png" /&gt;
&lt;p&gt;You see how the last pointer in each of the bottom nodes points to the next
node? This is used for something called &amp;#8220;Index Scan&amp;#8221;. I&amp;#8217;ll come back to that
later.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s look at the second row of nodes in a bit more detail.&lt;/p&gt;
&lt;img alt="B+ Tree leaf node pointers to table rows" src="/images/djangoconeu2017/slide15.png" /&gt;
&lt;p&gt;You can now see, how every pointer from one of those tree nodes points to a
single row in the table.&lt;/p&gt;
&lt;p&gt;You can also see how these pointers from the tree nodes somewhat randomly point
to some rows in the table. That&amp;#8217;s why this is called &amp;#8220;Random Access&amp;#8221;. The
database randomly jumps around in the database table.&lt;/p&gt;
&lt;h3&gt;Random Access Lookup&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s refresh our memory with the &lt;span class="caps"&gt;SQL&lt;/span&gt; query we had before.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;How does an index now help to find the corresponding row faster?&lt;/p&gt;
&lt;p&gt;Well, let&amp;#8217;s look at the tree:&lt;/p&gt;
&lt;img alt="B+ Tree random access" src="/images/djangoconeu2017/slide17.png" /&gt;
&lt;p&gt;It requires 1 step to get from the first node to the second node. And a seconds
step from the second node to the row in the database table.&lt;/p&gt;
&lt;p&gt;Remember, we had to look through all 7 rows before to see if they matched the
database query.&lt;/p&gt;
&lt;p&gt;And since there&amp;#8217;s no more key in the index with 19, we&amp;#8217;re done.&lt;/p&gt;
&lt;h3&gt;Index Scan&lt;/h3&gt;
&lt;p&gt;Now, coming back &amp;#8220;Index Scan&amp;#8221;, assume you want to count the amount of people
that started coding when they were in the age range from 5 to 13.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The database will look for the key 5 and will then use the pointer to the next
node to look for further keys.&lt;/p&gt;
&lt;img alt="B+ Tree index scan" src="/images/djangoconeu2017/slide19.png" /&gt;
&lt;p&gt;And because all the information that the database needs for the query is right
here in the index, the database will not look at the table at all.&lt;/p&gt;
&lt;h2&gt;Indexes Are Awesome&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s have them in Django.&lt;/p&gt;
&lt;p&gt;And we already do, actually.&lt;/p&gt;
&lt;p&gt;There is&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;db_index=True&lt;/tt&gt; which you can set on a model field&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;index_together=(('name',&lt;/span&gt; &lt;span class="pre"&gt;'age'),)&lt;/span&gt;&lt;/tt&gt; which you can set in a model&amp;#8217;s &lt;tt class="docutils literal"&gt;Meta&lt;/tt&gt;
class&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ForeignKey()&lt;/tt&gt; / &lt;tt class="docutils literal"&gt;OneToOneField()&lt;/tt&gt; which use indexes for quick lookups of
the data in related tables&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;primary_key=True&lt;/tt&gt; which Django automatically uses on the &lt;tt class="docutils literal"&gt;AutoField&lt;/tt&gt;
which represents the &lt;tt class="docutils literal"&gt;id&lt;/tt&gt; column on every model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is already great. But this feature set is somewhat limiting. There are not
just B+ Tree indexes out there. There are a ton more&lt;/p&gt;
&lt;h2&gt;2016&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s look at 2016.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/mjtamlyn"&gt;Marc Tamlyn&lt;/a&gt; and I had ideas for indexes.
Actually, Marc had some ideas during his &lt;tt class="docutils literal"&gt;contrib.postgres&lt;/tt&gt; work already. And
we had thoughts about APIs. And things we&amp;#8217;d like to have in Django. Like, let&amp;#8217;s
make Django support &lt;em&gt;all the indexes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But we didn&amp;#8217;t have time to implement our ideas!&lt;/p&gt;
&lt;p&gt;But we got lucky. Actually, the Django Project got lucky.&lt;/p&gt;
&lt;h2&gt;Google Summer of Code 2016&lt;/h2&gt;
&lt;p&gt;Django was, once again, accepted as an organization for Google Summer Of Code.
Thank you Google!&lt;/p&gt;
&lt;p&gt;For those of you who don&amp;#8217;t know what that is: Google pays a Student for 3
months to work on an Open Source project while being mentored by the project&amp;#8217;s
contributors.&lt;/p&gt;
&lt;p&gt;For the most part &lt;a class="reference external" href="https://github.com/timgraham"&gt;Tim Graham&lt;/a&gt;, but also Marc
and I mentored a student &lt;a class="reference external" href="https://github.com/akki"&gt;Akshesh Doshi&lt;/a&gt; tackling a
more generic Index support in Django. From writing down a proposal for the &lt;span class="caps"&gt;API&lt;/span&gt;
etc. until eventually merging things into Django.&lt;/p&gt;
&lt;p&gt;The major outcome of GSoC 2016 is &lt;tt class="docutils literal"&gt;django.db.models.indexes.Index(fields,
name)&lt;/tt&gt; (&lt;a class="reference external" href="https://docs.djangoproject.com/en/1.11/ref/models/indexes/#django.db.models.Index"&gt;docs&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;It defines the base class of all indexes. You can use them via the &lt;tt class="docutils literal"&gt;indexes&lt;/tt&gt;
option in a model&amp;#8217;s &lt;tt class="docutils literal"&gt;Meta&lt;/tt&gt; class.&lt;/p&gt;
&lt;p&gt;For example like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name_idx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create a B+ Tree index on the &lt;tt class="docutils literal"&gt;name&lt;/tt&gt; column of the database table.&lt;/p&gt;
&lt;p&gt;Granted, that&amp;#8217;s nothing new. That&amp;#8217;s what you can do with &lt;tt class="docutils literal"&gt;db_index=True&lt;/tt&gt; on
the name field.&lt;/p&gt;
&lt;p&gt;You can of course also define an Index on multiple columns:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveSmallIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name_age_idx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Granted, that&amp;#8217;s nothing new either. You can do with &lt;tt class="docutils literal"&gt;index_together&lt;/tt&gt; already.&lt;/p&gt;
&lt;p&gt;But you can now also do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.postgres.fields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JSONField&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.postgres.indexes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GinIndex&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JSONField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;GinIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_gin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Define a &lt;tt class="docutils literal"&gt;GinIndex&lt;/tt&gt;. That&amp;#8217;s something PostgreSQL specific. But it&amp;#8217;s something
you could not do before. At least not reliably without too much pain.&lt;/p&gt;
&lt;p&gt;A GinIndex can be used to index the key values inside a &lt;span class="caps"&gt;JSON&lt;/span&gt; blob. So you could
filter for rows in a table where the key in a &lt;span class="caps"&gt;JSONB&lt;/span&gt; field maps to a particular
value. That&amp;#8217;s like the &amp;#8220;NoSQL 1-0-1&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Another built-in index type that ships with Django 1.11 is &lt;tt class="docutils literal"&gt;BrinIndex&lt;/tt&gt; which,
simply put, can allow for much faster computation of aggregations. Such as,
finding the last time each article was purchased.&lt;/p&gt;
&lt;p&gt;And because Indexes are parts of the database schema, they are obviously
tracked through migrations. Thus the index is created when you run &lt;tt class="docutils literal"&gt;python
manage.py migrate&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Create model Doc&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;someapp_doc&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt; &lt;span class="nb"&gt;serial&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;data&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;jsonb&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="c1"&gt;-- Create index data_gin on field(s) data of model doc&lt;/span&gt;
&lt;span class="c1"&gt;--&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;data_gin&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="ss"&gt;&amp;quot;someapp_doc&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Feature ideas&lt;/h2&gt;
&lt;p&gt;Great.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s what is in Django 1.11 which was released &lt;a class="reference external" href="https://www.djangoproject.com/weblog/2017/apr/04/django-111-released/"&gt;yesterday&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But what&amp;#8217;s out there to come for Django 2.0?&lt;/p&gt;
&lt;p&gt;What&amp;#8217;s on the horizon?&lt;/p&gt;
&lt;p&gt;What do we want to have eventually?&lt;/p&gt;
&lt;h3&gt;Functional Indexes&lt;/h3&gt;
&lt;p&gt;They are useful in all kinds of situations where you don&amp;#8217;t want to index on the
raw value but e.g. on a variation of it, such as lower-case of a string. I&amp;#8217;m
already working on that. I&amp;#8217;m not quite there yet. I&amp;#8217;d love some help from
people understanding the expressions &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
           &lt;span class="n"&gt;FuncIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name_lower_idx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;db_index=&amp;lt;IndexClass&amp;gt;&lt;/span&gt;&lt;/tt&gt;&lt;/h3&gt;
&lt;p&gt;Using indexes for a single column can be cumbersome as you&amp;#8217;ve seen before.
Thus, let&amp;#8217;s support Index classes as an attribute to &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;db_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HashIndex&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;&lt;tt class="docutils literal"&gt;Field.default_index_class&lt;/tt&gt;&lt;/h3&gt;
&lt;p&gt;Having a B+ Tree for some fields doesn&amp;#8217;t make sense. As previously shown, there
is a &lt;tt class="docutils literal"&gt;GinIndex&lt;/tt&gt; that&amp;#8217;s perfect for &lt;tt class="docutils literal"&gt;JSONField&lt;/tt&gt;. Why not have a
&lt;tt class="docutils literal"&gt;default_index_class&lt;/tt&gt; per field class that&amp;#8217;s being used when
&lt;tt class="docutils literal"&gt;db_index=True&lt;/tt&gt;?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.postgres.fields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JSONField&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.postgres.indexes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GinIndex&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="c1"&gt;# Somewhere in Django’s JSONField implementation:&lt;/span&gt;
&lt;span class="c1"&gt;# JSONField.default_index_class = GinIndex&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JSONField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Refactor &lt;tt class="docutils literal"&gt;index_together&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt;&lt;/h3&gt;
&lt;p&gt;This one is more under the hood than user facing:&lt;/p&gt;
&lt;p&gt;It would probably make sense for &lt;tt class="docutils literal"&gt;db_index&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;index_together&lt;/tt&gt; to use
&lt;tt class="docutils literal"&gt;Model._meta.indexes&lt;/tt&gt; internally, I could imagine. That&amp;#8217;s something to
investigate.&lt;/p&gt;
&lt;h3&gt;&lt;tt class="docutils literal"&gt;GiSTIndex&lt;/tt&gt;&lt;/h3&gt;
&lt;p&gt;There&amp;#8217;s a &lt;tt class="docutils literal"&gt;GiSTIndex&lt;/tt&gt; in PostgreSQL that can be used for geo-spacial queries,
such as &amp;#8220;give me all points that have a maximum distance of 10 to a given
point&amp;#8221;. It&amp;#8217;s not in Django 1.11. I don&amp;#8217;t know why, but I guess because nobody
added it.&lt;/p&gt;
&lt;h3&gt;Sprints&lt;/h3&gt;
&lt;p&gt;And speaking about all these features, there are Sprints on Thursday and
Friday. I hope you are staying and join us.&lt;/p&gt;
&lt;p&gt;Talk to me or anybody else on the core team or any other attendee if you want
to work on any of those tickets. Some of those ideas still need tickets in our
&lt;a class="reference external" href="https://code.djangoproject.com/"&gt;issue tracker&lt;/a&gt;. And some also probably need
some discussion on the &lt;a class="reference external" href="https://groups.google.com/forum/#!forum/django-developers"&gt;django-developers mailing list&lt;/a&gt;. Now is the time
to get these discussions starting if you want to have any of this in Django
2.0.&lt;/p&gt;
&lt;p&gt;On that note, keep in mind that Django 2.0 will not support Python 2 anymore!&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/to-index-or-not-thats-not-the-question-djangocon-europe-2017"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Database"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category><category term="Python"></category><category term="Talk"></category></entry><entry><title>2 Factor Authentication in Django</title><link href="https://markusholtermann.eu/2016/09/2-factor-authentication-in-django/" rel="alternate"></link><published>2016-09-12T00:00:00+02:00</published><updated>2016-09-12T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-09-12:/2016/09/2-factor-authentication-in-django/</id><summary type="html">&lt;p class="first last"&gt;Most websites require some kind of authentication. Email &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; password
is common but sometimes not enough. In those cases &lt;span class="caps"&gt;2FA&lt;/span&gt; adds an additional
security layer.&lt;/p&gt;
</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;2 Factor Authentication &amp;#8212; or &lt;span class="caps"&gt;2FA&lt;/span&gt; in short &amp;#8212; is a special version of the
general Multi Factor Authentication (&lt;span class="caps"&gt;MFA&lt;/span&gt;) and generally describes the idea that
is also followed by Universal 2 Factor Authentication (&lt;span class="caps"&gt;U2F&lt;/span&gt;). It generally
consists of something a user possesses and something a user knows.&lt;/p&gt;
&lt;h2&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;I’m not a cryptographer. I’m somebody interested in security and cryptography.&lt;/p&gt;
&lt;h2&gt;History&lt;/h2&gt;
&lt;p&gt;Authentication is something we are confronted with every single day. At least
in the modern world. For most of us it probably starts the moment we look at
our phone in the morning and need to unlock it to get access to the feed
reader.&lt;/p&gt;
&lt;p&gt;But that’s today, what’s was there before? Maybe decades ago? Maybe centuries
ago?&lt;/p&gt;
&lt;p&gt;Around 300 &lt;span class="caps"&gt;BC&lt;/span&gt;, the Romans passed so called &amp;#8220;watchwords&amp;#8221; from to 10th maniple to
9th to the 8th and eventually to the 1st maniple in front of witnesses. The 1st
maniple then passed it back to the tribune who in return knew which group of
maniples didn&amp;#8217;t passed the watchword. The watchwords were used to authenticate
people during the night. &lt;a class="footnote-reference" href="#ancienthistory" id="id1"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Passwords evolved and In the opening days of the Battle of Normandy in 1944,
paratroopers of the &lt;span class="caps"&gt;US&lt;/span&gt; used a password — flash — which was presented as a
challenge, and answered with the correct response — thunder.&lt;/p&gt;
&lt;p&gt;But history shows that not only passwords have been used to authenticate people
or messages. The ancient Greek and Spartans are said to have used a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Scytale"&gt;scytale&lt;/a&gt; for encryption and for
authentication. A scytale is a cylinder with parchment wound around it. Only if
somebody had a cylinder of the same diameter the receiver could read the
message. That was even earlier, around 700 &lt;span class="caps"&gt;BC&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Today&lt;/h2&gt;
&lt;p&gt;In todays life, we have lots of 2 factor situations. Think about your Debit or
credit card. You posses that card. In order to use it you need to know the
related &lt;span class="caps"&gt;PIN&lt;/span&gt; you know. Only if you have both you can withdraw money from an &lt;span class="caps"&gt;ATM&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Think about logging into your Google, Github or many other major accounts: you
can set up your accounts on those services such that somebody logging in is
required to provide a &lt;span class="caps"&gt;PIN&lt;/span&gt; along the password. The &lt;span class="caps"&gt;PIN&lt;/span&gt; can for example be
generated by Google Authenticator.&lt;/p&gt;
&lt;p&gt;But there are many more identifying components that could be used. Think about
you fingerprints. Or the irises in your eyes. And don&amp;#8217;t forget your voice,
though modern computer simulations can imitate it fairly well.&lt;/p&gt;
&lt;p&gt;And lastly, there&amp;#8217;s still &lt;span class="caps"&gt;SMS&lt;/span&gt; as a second factor out there. However, if you
follow the &lt;span class="caps"&gt;NIST&lt;/span&gt; (National Institute of Standards and Technology), they recently
published a draft of a &lt;em&gt;Digital Authentication Guideline&lt;/em&gt;. In that it says:&lt;/p&gt;
&lt;blockquote&gt;
Due to the risk that &lt;span class="caps"&gt;SMS&lt;/span&gt; messages may be intercepted or redirected,
implementers of new systems &lt;span class="caps"&gt;SHOULD&lt;/span&gt; carefully consider alternative
authenticators. If the out of band verification is to be made using a &lt;span class="caps"&gt;SMS&lt;/span&gt;
message on a public mobile telephone network, the verifier &lt;span class="caps"&gt;SHALL&lt;/span&gt; verify that
the pre-registered telephone number being used is actually associated with a
mobile network and not with a VoIP (or other software-based) service.
&lt;a class="citation-reference" href="#nist" id="id2"&gt;[&lt;span class="caps"&gt;NIST&lt;/span&gt;]&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;What they are saying is, that you should verify that the receiver of an &lt;span class="caps"&gt;SMS&lt;/span&gt; is
the intended person and not somebody else interfering with the public mobile
phone network.&lt;/p&gt;
&lt;p&gt;If you’re looking into implementing &lt;span class="caps"&gt;2FA&lt;/span&gt;, I highly recommend to give this
document a read before.&lt;/p&gt;
&lt;h2&gt;The basics of 2 Factor Authentication&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s start with some basic, yet command and secure, processes for &lt;span class="caps"&gt;2FA&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;HOTP&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The first one, and the basis for the second one, is &lt;span class="caps"&gt;HOTP&lt;/span&gt; or &lt;span class="caps"&gt;HMAC&lt;/span&gt; based one time
passwords.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;HOTP&lt;/span&gt; sits on top of &lt;span class="caps"&gt;HMAC&lt;/span&gt;-&lt;span class="caps"&gt;SHA1&lt;/span&gt; &amp;#8212; which is still secure despite &lt;span class="caps"&gt;SHA1&lt;/span&gt; being
broken &amp;#8212; and some bit and byte shifting, called truncation. The full, in-depth
explanation is available in &lt;a class="reference external" href="https://tools.ietf.org/html/rfc4226"&gt;&lt;span class="caps"&gt;RFC&lt;/span&gt; 4226&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;HOTP&lt;/span&gt; is counter based. Each time you need a new one-time password you increase
a counter and get a new one-time-password. The key is know to you (or more
precisely your &lt;span class="caps"&gt;HOTP&lt;/span&gt; generator) and the verifying party. In the end, the
verifier has to make sure that each counter value can only be used once. That&amp;#8217;s
about it.&lt;/p&gt;
&lt;p&gt;The following code is from the &lt;a class="reference external" href="https://bitbucket.org/psagers/django-otp/src/acbccb8f621b00dda1d5018be54ab55e162586b3/django-otp/django_otp/oath.py"&gt;django-otp&lt;/a&gt;
package. Slightly reformatted.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hmac&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;struct&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hotp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;gt;Q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;hs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Truncate&lt;/span&gt;
    &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x0f&lt;/span&gt;
    &lt;span class="n"&gt;bin_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x7f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bin_code&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;&lt;span class="caps"&gt;TOTP&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The second algorithm I pointed out before is &lt;span class="caps"&gt;TOTP&lt;/span&gt;, Time-based one-time passwords.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;TOTP&lt;/span&gt; effectively works exactly the same as &lt;span class="caps"&gt;HOTP&lt;/span&gt; with one essential difference:
the counter is derived from the current time.&lt;/p&gt;
&lt;p&gt;What the algorithm does according to &lt;span class="caps"&gt;RFC&lt;/span&gt; &lt;a class="reference external" href="https://tools.ietf.org/html/rfc6238"&gt;&lt;span class="caps"&gt;RFC&lt;/span&gt; 6238&lt;/a&gt;, it takes the number of elapsed seconds
between now (t) and a start (t0) and divides it by the duration of how long a
single token should be valid. That defaults to 30 seconds. You effectively end
up with a counter that changes every 30 seconds.&lt;/p&gt;
&lt;p&gt;Looking at code, this is about it. Nothing fancy.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;totp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hotp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;YubiKey, Nitrokey&lt;/h3&gt;
&lt;p&gt;I’m not going into details about YubiKey, Nitrokey or similar systems.&lt;/p&gt;
&lt;p&gt;They are pieces of hardware, similar to &lt;span class="caps"&gt;USB&lt;/span&gt; sticks, that do crypto for you, for
example providing &lt;span class="caps"&gt;HOTP&lt;/span&gt; tokens, or other features such as &lt;span class="caps"&gt;SMIME&lt;/span&gt; or GnuPG keys.&lt;/p&gt;
&lt;h2&gt;Django Integration&lt;/h2&gt;
&lt;p&gt;Anyway, enough about the basics you got to know before you consider 2 Factor
Authentication for your project. How do you actually implement it? Here are 5
code snippets. I’ll implement a &lt;span class="caps"&gt;TOTP&lt;/span&gt; &amp;#8220;device&amp;#8221;, a view to enter and validate the
token as well as a decorator and mixin to require a 2 factor authenticated
session for a particular view.&lt;/p&gt;
&lt;p&gt;Let us start with the &lt;tt class="docutils literal"&gt;models.py&lt;/tt&gt;. Looking back at the parameters for the
&lt;span class="caps"&gt;TOTP&lt;/span&gt; function, you need a key, a step, the start time and last time a token was
used.&lt;/p&gt;
&lt;p&gt;In &lt;tt class="docutils literal"&gt;verify_token()&lt;/tt&gt; we check that the given token is valid by computing the
current token and checking the current token was generated after the last one
and matches the other token.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# models.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;binascii&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;new_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totp&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TOTPDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Adapted from django_otp&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AUTH_USER_MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;new_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveSmallIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BigIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BigIntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unhexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;totp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_t&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;last_t&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the view where we enter the token we need a form that has a single integer
field and validates the token against the &lt;span class="caps"&gt;TOTP&lt;/span&gt; device for a given user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# forms.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OTPTokenForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntegerField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;999999&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;totpdevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cleaned_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Invalid token&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The view to enter the token checks if the session is already verified &amp;#8212; that&amp;#8217;s
a key I defined &amp;#8212; and redirects to some other &lt;span class="caps"&gt;URL&lt;/span&gt; if it is.&lt;/p&gt;
&lt;p&gt;Otherwise it does normal form handling. For valid forms it updates the session
and redirects too.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# views.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;login_required&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.forms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OTPTokenForm&lt;/span&gt;

&lt;span class="nd"&gt;@login_required&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;otp_token_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;verified&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OTPTokenForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;verified&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OTPTokenForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;otp_token_form.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;form&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now for the decorator and mixin. If the user is not authenticated redirect them
to the login page. If the user is authenticated and the session has the
verified flag, use the view, otherwise redirect to the &lt;span class="caps"&gt;2FA&lt;/span&gt; view.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;Mind the property behavior of &lt;tt class="docutils literal"&gt;is_authenticated&lt;/tt&gt; here. That’s new in
Django 1.10. If you’re on 1.9 or before you need to make it function calls.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# decorators.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_verified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOGIN_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;verified&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;otp-token-view&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IsVerified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOGIN_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;verified&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;otp-token-view&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, and here’s how you finally use the decorator and mixin in views: For the
&lt;tt class="docutils literal"&gt;IsVerified&lt;/tt&gt; mixin, keep in mind that it has to go to the left! Mixins always
go left from the base view! This extends the&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# views.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.views.generic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;View&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;is_verified&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;.mixins&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IsVerified&lt;/span&gt;

&lt;span class="nd"&gt;@is_verified&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;This is a 2FA function view&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsVerified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;This is a 2FA class view&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, that was a lot of boilerplate. And there’s a bunch of stuff you don’t
even have yet.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;You don’t have a nice key setup.&lt;/li&gt;
&lt;li&gt;You don’t have a QRCode view that you can scan with your phone.&lt;/li&gt;
&lt;li&gt;You don’t have backup codes in case you ever lose you phone.&lt;/li&gt;
&lt;li&gt;You don’t have an integration into Django Admin.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Imagine to write all that yourself for every project you want to use 2 Factor
Authentication in. That’s not going to be fun. Hence there are people out there
that have done most of the work for you.&lt;/p&gt;
&lt;p&gt;django-otp provides the underlying algorithms and required database models.
Feel free to use it. django-otp ships with support for email, &lt;span class="caps"&gt;HOTP&lt;/span&gt;, static
tokens, &lt;span class="caps"&gt;TOTP&lt;/span&gt;. There are plug-ins for YubiKey and &lt;span class="caps"&gt;SMS&lt;/span&gt;, too. Reminder, don’t use
&lt;span class="caps"&gt;SMS&lt;/span&gt; if you can avoid it. There’s also a very simplistic Django admin
integration.&lt;/p&gt;
&lt;p&gt;Another library, that I co-maintain for a few months now, is &lt;a class="reference external" href="https://github.com/Bouke/django-two-factor-auth"&gt;django-two-
factor-auth&lt;/a&gt;. It heavily
reuses django-otp but adds a couple of more things to the party. There’s an
entire key setup process, including &lt;span class="caps"&gt;QR&lt;/span&gt; code view, backup codes. There’s a
separate implementation for Django admin, not tied to django-otp. I’m also
working on an advanced admin integration which includes theming and so on. If
you’re staying for the sprints and are looking for something to work on, I’m
happy to guide you through the process.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s the future of 2 Factor Authentication?&lt;/h2&gt;
&lt;p&gt;Given what I’ve talked about, where can or should you go?&lt;/p&gt;
&lt;p&gt;In my opinion, when you deal with sensitive personal information in your
projects, you probably want 2 Factor Authentication. When you deal with other
people’s money, you probably want 2 Factor Authentication. When you deal with
sensitive infrastructure, you probably want 2 Factor Authentication. When you
deal with anything that you think might be worth it, you probably want 2 Factor
Authentication. When you deal with anything else, why not have 2 Factor
Authentication as an opt-in at least?&lt;/p&gt;
&lt;p&gt;How you get there is up to you. I showed you a simple way to build the basics
yourself. If that’s what you prefer, sure, please use that. If you prefer to
rely on other projects, great, do it and report and contribute back as a “thank
you”, please.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s also the option for Universal 2 Factor Authentication I mentioned in
the beginning. &lt;a class="reference external" href="https://jeman.de/"&gt;Yuriy Ackermann&lt;/a&gt; gave a &lt;a class="reference external" href="https://www.youtube.com/watch?v=Mxk3ueCkZG8"&gt;talk about that
at Kiwi PyCon 2016&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;Lastly, why would you want to offer &lt;span class="caps"&gt;2FA&lt;/span&gt; to your customers? Kenneth Reitz
recently &lt;a class="reference external" href="http://www.kennethreitz.org/essays/on-cybersecurity-and-being-targeted"&gt;published a post&lt;/a&gt; how &lt;span class="caps"&gt;2FA&lt;/span&gt; saved a couple of accounts from
being taken over.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/django-and-2-factor-authentication"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=7Z8cDOCH5fM"&gt;Video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table class="docutils footnote" frame="void" id="ancienthistory" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://ancienthistory.about.com/library/bl/bl_text_polybius6.htm"&gt;http://ancienthistory.about.com/library/bl/bl_text_polybius6.htm&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="nist" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id2"&gt;[&lt;span class="caps"&gt;NIST&lt;/span&gt;]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://pages.nist.gov/800-63-3/sp800-63b.html"&gt;https://pages.nist.gov/800-63-3/sp800-63b.html&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Security"></category><category term="Talk"></category></entry><entry><title>SSL All The Things In Python</title><link href="https://markusholtermann.eu/2016/09/ssl-all-the-things-in-python/" rel="alternate"></link><published>2016-09-10T00:00:00+02:00</published><updated>2016-09-10T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-09-10:/2016/09/ssl-all-the-things-in-python/</id><summary type="html">&lt;p class="first last"&gt;Getting &lt;span class="caps"&gt;SSL&lt;/span&gt; / &lt;span class="caps"&gt;TLS&lt;/span&gt; right is hard. As an update to my DjangoCon &lt;span class="caps"&gt;US&lt;/span&gt; 2016
talk here&amp;#8217;s how you use Python&amp;#8217;s &lt;span class="caps"&gt;SSL&lt;/span&gt; library.&lt;/p&gt;
</summary><content type="html">&lt;img alt="PyCon AU 2016 -- Melbourne" class="margin-left align-right" src="/images/pyconau2016/logo.png" /&gt;
&lt;img alt="Kiwi PyCon 2016 -- Dunedin" class="clearfix margin-left align-right" src="/images/pyconnz2016/logo.png" /&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Following my &lt;a class="reference external" href="https://markusholtermann.eu/2016/07/ssl-all-the-things/"&gt;DjangoCon &lt;span class="caps"&gt;US&lt;/span&gt; 2016 Talk&lt;/a&gt; I gave a talk
at PyCon Australia in Melbourne and PyCon New Zealand in Dunedin.&lt;/p&gt;
&lt;p&gt;Most of the slides from the former talk are the same. The major difference is
the replacement of Django specifics with examples for a Python client and
server implementation using the &lt;tt class="docutils literal"&gt;ssl&lt;/tt&gt; standard library package.&lt;/p&gt;
&lt;h2&gt;How to use &lt;span class="caps"&gt;SSL&lt;/span&gt; in Python&lt;/h2&gt;
&lt;h3&gt;Client Side&lt;/h3&gt;
&lt;p&gt;The client side of &lt;span class="caps"&gt;SSL&lt;/span&gt; connections is comparably easy as opposed to the server
side. That doesn&amp;#8217;t mean you can&amp;#8217;t easily mess up things and make the entire
encryption layer you intend to have useless. But what would security be without
some danger, right 😉.&lt;/p&gt;
&lt;p&gt;First of all you create a socket. That&amp;#8217;s the normal Python socket layer. You
then create a default &lt;span class="caps"&gt;SSL&lt;/span&gt; context which has all the best practices of the
installed Python version. You can furthermore drop some protocols if you feel
like it and your requirements allow for that. Next you wrap the socket into an
&lt;span class="caps"&gt;SSL&lt;/span&gt; socket. While you do that, make sure you check the host name! And finally
you connect to the server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ssl&lt;/span&gt;

&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GET / HTTP/1.1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Purpose&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SERVER_AUTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_TLSv1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_TLSv1_1&lt;/span&gt;  &lt;span class="c1"&gt;# optional&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap_socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Server Side&lt;/h3&gt;
&lt;p&gt;The server side of &lt;span class="caps"&gt;SSL&lt;/span&gt; connections is more complicated and you can mess up much
more. But let us start at the beginning.&lt;/p&gt;
&lt;p&gt;You create a socket and bind it to an address and port. You, again, create a
&lt;span class="caps"&gt;SSL&lt;/span&gt; default context for a server.  The first thing you probably want to do is
load the key, certificate and all intermediate certificates. You can put all of
these into one file in that order. You can also provide them as separate
arguments to the &lt;tt class="docutils literal"&gt;load_cert_chain()&lt;/tt&gt; function. Again, you can exclude some
protocols and update other context options based on your requirements. In the
example we&amp;#8217;re disabling &lt;span class="caps"&gt;TLS&lt;/span&gt; 1.0 and 1.1, because nobody needs them, right 😉.
Lastly, you want to set a bunch of ciphers. That list comes from &lt;a class="reference external" href="https://cipherli.st"&gt;cipherli.st&lt;/a&gt;. Finally you start listening on the socket and wrap it
with the &lt;span class="caps"&gt;SSL&lt;/span&gt; context for each connection.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ssl&lt;/span&gt;

&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CERT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;example.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/path/to/example.com.pem&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getpeername&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Purpose&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLIENT_AUTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_cert_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;certfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CERT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 1. key, 2. cert, 3. intermediates&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_TLSv1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OP_NO_TLSv1_1&lt;/span&gt;  &lt;span class="c1"&gt;# optional&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_ciphers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;ssock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap_socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SSLError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/ssl-all-the-things-pycon-au-2016"&gt;Slides PyCon &lt;span class="caps"&gt;AU&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/ssl-all-the-things-pycon-nz-2016"&gt;Slides PyCon &lt;span class="caps"&gt;NZ&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Network"></category><category term="PyCon"></category><category term="Security"></category><category term="Server"></category><category term="Talk"></category></entry><entry><title>DjangoCon US 2016: SSL All The Things</title><link href="https://markusholtermann.eu/2016/07/ssl-all-the-things/" rel="alternate"></link><published>2016-07-19T00:00:00+02:00</published><updated>2016-07-19T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-07-19:/2016/07/ssl-all-the-things/</id><summary type="html">&lt;p class="first last"&gt;Over the last few years &lt;span class="caps"&gt;SSL&lt;/span&gt;/&lt;span class="caps"&gt;TLS&lt;/span&gt; encryption of websites has risen
tremendously. The Let’s Encrypt organization makes that pretty easy.  And
you can use it.&lt;/p&gt;
</summary><content type="html">&lt;img alt="DjangoCon 2016 -- Philadelphia" class="margin-left align-right" src="/images/djangoconus2016/logo.png" /&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Ever since Snowden revealed that the &lt;span class="caps"&gt;NSA&lt;/span&gt; is spying on all of us the urge to
more secure systems increased. At least over in Germany and plenty other
European countries.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s also the risk of public and unencrypted Wi-Fis. You don&amp;#8217;t want the
owner of the cafe next door or the other people sitting in the cafe be able to
wiretap your network traffic.&lt;/p&gt;
&lt;p&gt;And some internet service providers even inject their own advertisement in
websites you access. That&amp;#8217;s scary. And a bad idea from a user&amp;#8217;s perspective.
Not to mention from a security perspective.&lt;/p&gt;
&lt;h2&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m not a cryptographer. All examples I give here are either to the best of my
knowledge or I&amp;#8217;ve talked to skilled people I trust and asked them for their
knowledge. Which still means there can be errors as we all a humans!&lt;/p&gt;
&lt;p&gt;This blog post will not cover everything you could possibly know about &lt;span class="caps"&gt;SSL&lt;/span&gt; and
&lt;span class="caps"&gt;TLS&lt;/span&gt;, by all means, that would be enough to fill an entire conference with
talks.&lt;/p&gt;
&lt;p&gt;Also, &lt;span class="caps"&gt;SSL&lt;/span&gt; 2 and 3 are broken and insecure. Do not use them! Period. &lt;span class="caps"&gt;TLS&lt;/span&gt; 1.0 and
1.1 are discouraged and superseded and should also not be used anymore as well.
Unless you really need to &lt;a class="reference external" href="https://www.ssllabs.com/ssltest/clients.html"&gt;support old browsers&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What is &lt;span class="caps"&gt;SSL&lt;/span&gt; / &lt;span class="caps"&gt;TLS&lt;/span&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;span class="caps"&gt;SSL&lt;/span&gt; stands for Secure Socket Layer. &lt;span class="caps"&gt;TLS&lt;/span&gt; stands for Transport Layer Security.
But that&amp;#8217;s just words. They are both cryptographic protocols for communication
systems most notably networks. Either one provides 2 of the 3 parts of
&amp;#8220;Information security&amp;#8221;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Confidentiality which is provided through encryption&lt;/li&gt;
&lt;li&gt;Integrity which is provided through signing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Availability is the third part in Information Security but needs to be handled
on a different layer.&lt;/p&gt;
&lt;h2&gt;Web Server Configuration&lt;/h2&gt;
&lt;p&gt;Because this is the blog post for my talk from DjangoCon &lt;span class="caps"&gt;US&lt;/span&gt; 2016 and most
things we do with Django these days are website, let us have a look at how to
configure web servers to handle &lt;span class="caps"&gt;SSL&lt;/span&gt;.&lt;/p&gt;
&lt;h3&gt;Apache 2 / httpd&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s start with Apache 2 or httpd&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;*:443&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;ServerName&lt;/span&gt;  example.com
    &lt;span class="nb"&gt;SSLEngine&lt;/span&gt;   &lt;span class="k"&gt;on&lt;/span&gt;

    &lt;span class="c"&gt;# Details at https://cipherli.st/&lt;/span&gt;
    &lt;span class="nb"&gt;SSLCipherSuite&lt;/span&gt;         EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
    &lt;span class="nb"&gt;SSLHonorCipherOrder&lt;/span&gt;    &lt;span class="k"&gt;on&lt;/span&gt;
    &lt;span class="nb"&gt;SSLProtocol&lt;/span&gt;            &lt;span class="k"&gt;all&lt;/span&gt; -SSLv3
    &lt;span class="nb"&gt;SSLCertificateFile&lt;/span&gt;     &lt;span class="sx"&gt;/etc/nginx/ssl/example.com.crt&lt;/span&gt;
    &lt;span class="nb"&gt;SSLCertificateKeyFile&lt;/span&gt;  &lt;span class="sx"&gt;/etc/nginx/ssl/example.com.key&lt;/span&gt;
    &lt;span class="nb"&gt;SSLOpenSSLConfCmd&lt;/span&gt;      DHParameters   &lt;span class="s2"&gt;&amp;quot;/etc/nginx/ssl/example.com.dh&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You need to switch on the &lt;span class="caps"&gt;SSL&lt;/span&gt; engine. You want to define which ciphers you want
and make sure the connection is going to use the best cipher it can, according
to &lt;strong&gt;your&lt;/strong&gt; list, not the client&amp;#8217;s.&lt;/p&gt;
&lt;p&gt;You also want to ensure to not use &lt;span class="caps"&gt;SSL&lt;/span&gt; and only &lt;span class="caps"&gt;TLS&lt;/span&gt; and maybe go for &lt;span class="caps"&gt;TLS&lt;/span&gt; 1.2
only.&lt;/p&gt;
&lt;p&gt;Defining a custom Diffie-Hellman Parameter is also a good idea. That is used
for the initial key exchange between a client and a server.&lt;/p&gt;
&lt;h3&gt;Nginx&lt;/h3&gt;
&lt;p&gt;And this is the syntax for Nginx. It looks pretty much the same as the one for
Apache 2.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt;       &lt;span class="s"&gt;[::]:443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt;  &lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Details at https://cipherli.st/&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_ciphers&lt;/span&gt;                &lt;span class="s"&gt;EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_prefer_server_ciphers&lt;/span&gt;  &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_protocols&lt;/span&gt;              &lt;span class="s"&gt;TLSv1&lt;/span&gt; &lt;span class="s"&gt;TLSv1.1&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt;            &lt;span class="s"&gt;/etc/nginx/ssl/example.com.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt;        &lt;span class="s"&gt;/etc/nginx/ssl/example.com.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_dhparam&lt;/span&gt;                &lt;span class="s"&gt;/etc/nginx/ssl/example.com.dh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What is Let&amp;#8217;s Encrypt?&lt;/h2&gt;
&lt;p&gt;Now that we know how to configure web servers where the heck do we get the
certificates from?&lt;/p&gt;
&lt;p&gt;And what the heck is &amp;#8220;&lt;a class="reference external" href="https://letsencrypt.org/"&gt;Let&amp;#8217;s Encrypt&lt;/a&gt;&amp;#8220;?&lt;/p&gt;
&lt;p&gt;And how does the whole &lt;span class="caps"&gt;SSL&lt;/span&gt; certificate thing work anyway??&lt;/p&gt;
&lt;p&gt;In order to answer these questions I need to tell you a bit about how the
entire certificate machinery works.&lt;/p&gt;
&lt;h2&gt;How does &lt;span class="caps"&gt;SSL&lt;/span&gt; work?&lt;/h2&gt;
&lt;div class="row gallery"&gt;
&lt;a class="offset-l4 col s12 m6 l4" href="/images/djangoconus2016/truststore.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconus2016/truststore-315x315-nocrop.jpg" alt="Certificate Authorities and Trust Store concept in browsers" title="Certificate Authorities and Trust Store concept in browsers"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconus2016/truststore-364x364-nocrop.jpg" alt="Certificate Authorities and Trust Store concept in browsers" title="Certificate Authorities and Trust Store concept in browsers"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconus2016/truststore-480x480-nocrop.jpg" alt="Certificate Authorities and Trust Store concept in browsers" title="Certificate Authorities and Trust Store concept in browsers"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Let us start with CAs: &lt;span class="caps"&gt;CA&lt;/span&gt; stands for Certificate Authority. These are &amp;#8220;trusted&amp;#8221;
entities we or our browsers rely on in order to establish a &lt;em&gt;chain of trust&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Browsers, email clients, and other programs have a pre-installed set of root
certificates. These are provided by Root CAs, CAs that e.g. browser vendors &amp;#8212;
such as Mozilla and Google &amp;#8212; trust. That database of root certificates is the
&amp;#8220;trust store&amp;#8221;.&lt;/p&gt;
&lt;p&gt;The Root CAs sign intermediate certificates. Most Root CAs just sign their own
intermediate certificates. Some Root CAs though (in the diagram &amp;#8220;Root &lt;span class="caps"&gt;CA&lt;/span&gt; 3&amp;#8221;)
are not in the trust store. So they are not automatically trusted by our
browsers.&lt;/p&gt;
&lt;p&gt;Trusted Root CAs can &lt;em&gt;cross sign&lt;/em&gt; other &lt;span class="caps"&gt;CA&lt;/span&gt;&amp;#8217;s intermediate certificates and make
certificates signed by &amp;#8220;Intermediate &lt;span class="caps"&gt;CA&lt;/span&gt; 3&amp;#8221; trusted by a browser.&lt;/p&gt;
&lt;p&gt;Which brings me to the next point: the certificate you can buy from well known
vendors are not signed by their root certificates but are signed by the
intermediate certificates.&lt;/p&gt;
&lt;p&gt;As a result, when your browser trusts Root Cert 1, it trusts Intermediate Cert
1, and hence it trusts all certificates signed by Intermediate Cert 1.&lt;/p&gt;
&lt;p&gt;If your browser trusts Root Cert 2, it trusts Intermediate Cert 3, and hence it
trusts all certificates signed by Intermediate Cert 3.&lt;/p&gt;
&lt;h2&gt;What is Let&amp;#8217;s Encrypt?&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s a Root &lt;span class="caps"&gt;CA&lt;/span&gt;, that&amp;#8217;s not in global trust stores. At least not yet. Their
Intermediate certificate is cross-signed by IdenTrust which is in all common
trust stores.&lt;/p&gt;
&lt;p&gt;But Let&amp;#8217;s Encrypt has control over their intermediate Cert, which allows them
to sign arbitrary other certificates, which we will use for our servers.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s Encrypt offers an &lt;span class="caps"&gt;API&lt;/span&gt; to get &amp;#8220;unlimited&amp;#8221; &lt;span class="caps"&gt;SSL&lt;/span&gt; certificates for your
domains, free of charge.&lt;/p&gt;
&lt;p&gt;By now they issued more than 5 million certificates through their &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The process during which one can get a certificate is defined as &lt;a class="reference external" href="https://github.com/letsencrypt/acme-spec"&gt;&lt;span class="caps"&gt;ACME&lt;/span&gt;&lt;/a&gt; or Automatic Certificate
Management Environment.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s a fairly simple &lt;span class="caps"&gt;JSON&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; with some crypto magic separated in 4 major
steps.&lt;/p&gt;
&lt;p&gt;In order to use the Let&amp;#8217;s Encrypt &lt;span class="caps"&gt;API&lt;/span&gt; one needs three things:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;An Account Key &amp;#8212; This is most likely an &lt;span class="caps"&gt;RSA&lt;/span&gt; public key pair&lt;/li&gt;
&lt;li&gt;A Certificate Key &amp;#8212; That&amp;#8217;s the key you put in your web server config&lt;/li&gt;
&lt;li&gt;A Certificate Signing Requests (&lt;span class="caps"&gt;CSR&lt;/span&gt;) &amp;#8212; This contains the list of domains
you want to be part of the certificate&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="row gallery"&gt;
&lt;a class="offset-l4 col s12 m6 l4" href="/images/djangoconus2016/acme.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconus2016/acme-315x315-nocrop.jpg" alt="The ACME process" title="The ACME process"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconus2016/acme-364x364-nocrop.jpg" alt="The ACME process" title="The ACME process"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconus2016/acme-480x480-nocrop.jpg" alt="The ACME process" title="The ACME process"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;The first &lt;span class="caps"&gt;API&lt;/span&gt; endpoint is &amp;#8220;new-reg&amp;#8221;. This authenticates &lt;em&gt;you&lt;/em&gt; as a &amp;#8220;person&amp;#8221; or
&amp;#8220;server&amp;#8221; against Let&amp;#8217;s Encrypt. You send your Account Key&amp;#8217;s public key signed
with the private key to Let&amp;#8217;s Encrypt. You can optionally include a mail
address to get notified about expired certificates.&lt;/p&gt;
&lt;p&gt;Second, there is &amp;#8220;new-authz&amp;#8221;. You send your &lt;span class="caps"&gt;CSR&lt;/span&gt; to Let&amp;#8217;s Encrypt signed with
your Account Key. The &lt;span class="caps"&gt;API&lt;/span&gt; responds with a list of challenges. These are
specially crafted URLs, with specific content, under each requested domain, you
need to make available. This is the step to proof you have control over the
content served under a given domain.&lt;/p&gt;
&lt;p&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;challenge&amp;#8221; is the next step. For each challenge you received, Let&amp;#8217;s Encrypt is
going to request the related challenge you made available and check for its
correctness.&lt;/p&gt;
&lt;p&gt;Lastly, &amp;#8220;new-cert&amp;#8221;. When all challenges succeeded you can request the
certificate and put that in the place where your web server finds it.&lt;/p&gt;
&lt;p&gt;And here&amp;#8217;s the Apache 2 / httpd config to make those challenges available&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;*:80&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nb"&gt;ServerName&lt;/span&gt;  example.com

    &lt;span class="nb"&gt;Redirect&lt;/span&gt;    /  https://example.com/

    &lt;span class="nb"&gt;Alias&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/.well-known/acme-challenge/&amp;quot;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/srv/http/acme-challenges/&amp;quot;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/srv/http/acme-challenges&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nb"&gt;AllowOverride&lt;/span&gt; &lt;span class="k"&gt;None&lt;/span&gt;
        &lt;span class="nb"&gt;Options&lt;/span&gt; &lt;span class="k"&gt;None&lt;/span&gt;
        &lt;span class="nb"&gt;Require&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt; granted
    &lt;span class="nt"&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the same one for Nginx.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt;       &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt;  &lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/.well-known/acme-challenge/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;alias&lt;/span&gt;      &lt;span class="s"&gt;/srv/http/acme-challenges/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt;  &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt;  &lt;span class="mi"&gt;301&lt;/span&gt;  &lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="nv"&gt;$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;How to use Let&amp;#8217;s Encrypt?&lt;/h2&gt;
&lt;p&gt;There&amp;#8217;s an official client which does &amp;#8220;all&amp;#8221; the magic. Literally, it rewrites
your Apache config file.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s the wrong approach, in my opinion. That&amp;#8217;s a task for configuration and
system management tools. &lt;a class="reference external" href="https://markusholtermann.eu/2015/08/the-necessity-of-configuration-and-system-management-tools/"&gt;Watch my talk from 2015th PyCon Australia&lt;/a&gt;
for details on that topic.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a script &lt;a class="reference external" href="https://github.com/diafygi/acme-tiny"&gt;acme-tiny by Daniel Roesler&lt;/a&gt; which has 200 lines. Easy to
understand. I Recommended to read through the code. &lt;a class="reference external" href="https://github.com/MarkusH/acme-tiny/tree/systemd"&gt;I forked that&lt;/a&gt; and added support for
Systemd and a few other things.&lt;/p&gt;
&lt;p&gt;There are also a bunch of other tools. E.g. &lt;a class="reference external" href="https://github.com/alex/letsencrypt-aws"&gt;letsencrypt-aws
by Alex Gaynor&lt;/a&gt;. I haven&amp;#8217;t used it
myself, but it probably just does what the name suggests.&lt;/p&gt;
&lt;p&gt;And there&amp;#8217;s &lt;a class="reference external" href="https://github.com/hawkowl/rproxy"&gt;Rproxy by Amber Brown&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Personally and at djangoproject.com we use my fork of acme-tiny.&lt;/p&gt;
&lt;p&gt;But all the scripts boil down to the same things: You need an account key, a
certificate signing request, a place to put the challenges, and a final
location for the full certificate.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python3 /etc/acme-tiny/acme-tiny.py &lt;span class="se"&gt;\&lt;/span&gt;
    --account-key &lt;span class="s2"&gt;&amp;quot;/etc/acme-tiny/account.key&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    --csr &lt;span class="s2"&gt;&amp;quot;/etc/acme-tiny/example.com.csr&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    --acme-dir &lt;span class="s2"&gt;&amp;quot;/srv/www/acme-challenges&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    --output &lt;span class="s2"&gt;&amp;quot;/etc/nginx/ssl/example.com.crt&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    --combine &lt;span class="s2"&gt;&amp;quot;https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Adjusting Django&lt;/h2&gt;
&lt;p&gt;Now that this is sorted, how do we use &lt;span class="caps"&gt;HTTPS&lt;/span&gt; in Django and what do you need to
change?&lt;/p&gt;
&lt;p&gt;As it turns out, not much.&lt;/p&gt;
&lt;p&gt;You want to make cookies only accessible via &lt;span class="caps"&gt;HTTPS&lt;/span&gt;. But only on production
systems. You can&amp;#8217;t use &lt;span class="caps"&gt;HTTPS&lt;/span&gt; locally at least not without some complex steps.
At least not with built-in features. There is, however, a &lt;tt class="docutils literal"&gt;runserver_plus&lt;/tt&gt;
command in the &lt;a class="reference external" href="https://github.com/django-extensions/django-extensions"&gt;django-extensions&lt;/a&gt; 3rd party app.
Hence, set the settings variables through environment variables:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;CSRF_COOKIE_SECURE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SECURE_COOKIES&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;SESSION_COOKIE_SECURE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SECURE_COOKIES&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yes&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you use secure cookies with the runserver command this might manifest as a
&lt;span class="caps"&gt;HTTP&lt;/span&gt; 403 error when you try to login to the admin because the &lt;span class="caps"&gt;CSRF&lt;/span&gt; cookie won&amp;#8217;t
be submitted.&lt;/p&gt;
&lt;p&gt;Apart from that, there a couple of things you can optionally enable.  Have a
look at the &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/topics/security/"&gt;Security Topic in the Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most things you don&amp;#8217;t want to do in Django though, but in your reverse proxy if
at all possible. It&amp;#8217;ll be faster.&lt;/p&gt;
&lt;h2&gt;What I didn&amp;#8217;t cover &amp;#8230; but want to mention&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s Encrypt&amp;#8217;s certificates are only valid for 90 days. If you need to revoke
one before, there&amp;#8217;s an &lt;span class="caps"&gt;API&lt;/span&gt; call for that.&lt;/p&gt;
&lt;p&gt;If you lost your account key or it was compromised you can change it as well.&lt;/p&gt;
&lt;p&gt;You probably want &lt;a class="reference external" href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security"&gt;&lt;span class="caps"&gt;HSTS&lt;/span&gt; (&lt;span class="caps"&gt;HTTP&lt;/span&gt; Strict Transport Security)&lt;/a&gt;. Really.
Running a website on &lt;span class="caps"&gt;HTTP&lt;/span&gt; and &lt;span class="caps"&gt;HTTPS&lt;/span&gt; may make it possible for attackers to force
&lt;span class="caps"&gt;HTTP&lt;/span&gt; requests.&lt;/p&gt;
&lt;p&gt;Similar to &lt;span class="caps"&gt;HSTS&lt;/span&gt; there&amp;#8217;s also &lt;a class="reference external" href="https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning"&gt;&lt;span class="caps"&gt;HPKP&lt;/span&gt; (&lt;span class="caps"&gt;HTTP&lt;/span&gt; Public Key Pinning)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This feature is neat but mostly not too useful unless you&amp;#8217;re a big organization
and you are regular victim of &lt;span class="caps"&gt;DNS&lt;/span&gt; attacks.&lt;/p&gt;
&lt;p&gt;And of course you can use the Let&amp;#8217;s Encrypy certificates for services other
than &lt;span class="caps"&gt;HTTPS&lt;/span&gt;. The only thing you need to make sure is that the domains for that
certificate serve valid challenge responses on port 80 as explained above.&lt;/p&gt;
&lt;h2&gt;Things that could go wrong &amp;#8212; An incomplete list&lt;/h2&gt;
&lt;p&gt;And because we&amp;#8217;re talking about cryptographic tropic, here&amp;#8217;s an incomplete list
of things that can go wrong:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Both, &lt;span class="caps"&gt;HSTS&lt;/span&gt; and &lt;span class="caps"&gt;HPKP&lt;/span&gt; come with a risk of making your website unavailable for
previous users. Check out how it works. You want it, but be aware of the risks!&lt;/li&gt;
&lt;li&gt;When you deal with cryptographic keys, there&amp;#8217;s always a possibility to leak
keys. Be aware of that and have a process in place to handle the case of a
leak certificate or account key!&lt;/li&gt;
&lt;li&gt;Some people claim &lt;span class="caps"&gt;HTTPS&lt;/span&gt; is causing too much resourcen usage and they can&amp;#8217;t use
it. With not too old hardware that&amp;#8217;s not an issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/ssl-all-the-things-djangocon-us-2016"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://cipherli.st/"&gt;https://cipherli.st/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.ssllabs.com/ssltest/index.html"&gt;https://www.ssllabs.com/ssltest/index.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://hynek.me/talks/tls/"&gt;https://hynek.me/talks/tls/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://ssldecoder.org/"&gt;https://ssldecoder.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://securityheaders.io/"&gt;https://securityheaders.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/ietf-wg-acme/acme/blob/bf34c2a/draft-ietf-acme-acme.md"&gt;https://github.com/ietf-wg-acme/acme/blob/bf34c2a/draft-ietf-acme-acme.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://security.googleblog.com/2016/07/experimenting-with-post-quantum.html"&gt;https://security.googleblog.com/2016/07/experimenting-with-post-quantum.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Network"></category><category term="Nginx"></category><category term="Security"></category><category term="Server"></category><category term="Talk"></category></entry><entry><title>Reference variables in Ansible YAML</title><link href="https://markusholtermann.eu/2016/04/reference-variables-in-ansible-yaml/" rel="alternate"></link><published>2016-04-11T00:00:00+02:00</published><updated>2016-04-11T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-04-11:/2016/04/reference-variables-in-ansible-yaml/</id><summary type="html">&lt;p class="first last"&gt;Mostly unknown, but the &lt;span class="caps"&gt;YAML&lt;/span&gt; syntax used by e.g. Ansible allows
convenient references to other variables.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Today I found myself in a typical &lt;span class="caps"&gt;DRY&lt;/span&gt; situation when working on an Ansible
role. I was defining a slightly more complex variable to later be used in an
Ansible template. The variable looked a bit like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;backups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;primary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/extern&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secondary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/intern&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;remote&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ssh://user@host:/mnt/data/backup/client1&amp;quot;&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;grab-n-go&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/usb-drive&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/markus/documents/important&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can easily see, the &lt;tt class="docutils literal"&gt;includes&lt;/tt&gt; item always contains the same values
for the first 3 items. I looked into the possibility to define the list of
directories only once and use a variable in those places.&lt;/p&gt;
&lt;p&gt;I started off with this approach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;backup_includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;

&lt;span class="nt"&gt;backups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;primary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/extern&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secondary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/intern&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;remote&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ssh://user@host:/mnt/data/backup/client1&amp;quot;&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;grab-n-go&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/usb-drive&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/markus/documents/important&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Turns out, that does not work. Instead, when iterating over
&lt;tt class="docutils literal"&gt;backups.X.includes&lt;/tt&gt; Jinja2 is iterating over the string &lt;tt class="docutils literal"&gt;backup_includes&lt;/tt&gt;.
That&amp;#8217;s of course not what I want.&lt;/p&gt;
&lt;p&gt;But &lt;span class="caps"&gt;YAML&lt;/span&gt; does provide a solution to reference other variables, which I found on
&lt;a class="reference external" href="http://stackoverflow.com/a/18877077"&gt;StackOverflow&lt;/a&gt;. It&amp;#8217;s also shown on
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/YAML#Repeated_nodes"&gt;Wikipedia&lt;/a&gt;. Using &lt;tt class="docutils literal"&gt;&amp;amp;&lt;/tt&gt;
to define an &lt;strong&gt;anchor&lt;/strong&gt; and &lt;tt class="docutils literal"&gt;*&lt;/tt&gt; to dereference it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;backup_includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;

&lt;span class="nt"&gt;backups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;primary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/extern&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secondary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/intern&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;remote&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ssh://user@host:/mnt/data/backup/client1&amp;quot;&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*backup_includes&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;grab-n-go&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/usb-drive&lt;/span&gt;
    &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/markus/documents/important&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apart from &lt;tt class="docutils literal"&gt;&amp;amp;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;*&lt;/tt&gt; there&amp;#8217;s also the &lt;tt class="docutils literal"&gt;&amp;lt;&amp;lt;:&lt;/tt&gt; operator to clone an item
and allow overriding of its child attributes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;backup_item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;backup_item&lt;/span&gt;
  &lt;span class="nt"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/&lt;/span&gt;
    &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/&lt;/span&gt;
    &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;
  &lt;span class="nt"&gt;excludes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p p-Indicator"&gt;[]&lt;/span&gt;

&lt;span class="nt"&gt;backups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*backup_item&lt;/span&gt;
    &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;primary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/extern&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*backup_item&lt;/span&gt;
    &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secondary&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/mnt/data/intern&lt;/span&gt;
  &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*backup_item&lt;/span&gt;
    &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;remote&lt;/span&gt;
    &lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ssh://user@host:/mnt/data/backup/client1&amp;quot;&lt;/span&gt;
    &lt;span class="nt"&gt;excludes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="Linux"></category><category term="Ansible"></category><category term="YAML"></category></entry><entry><title>Django Migrations Recipe #1</title><link href="https://markusholtermann.eu/2016/04/django-migrations-recipe-1/" rel="alternate"></link><published>2016-04-04T00:00:00+02:00</published><updated>2016-05-02T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-04-04:/2016/04/django-migrations-recipe-1/</id><summary type="html">&lt;p class="first last"&gt;This recipe shows how one can optimize a migration Django creates.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is the first migration recipe I publish for Django&amp;#8217;s migration framework.
I published it as part of my &lt;a class="reference external" href="https://markusholtermann.eu/2016/04/dont-be-afraid-of-writing-migrations/"&gt;talk at DjangoCon Europe 2016 in Budapest&lt;/a&gt;. This recipe will show that migration code is just Python and
Django&amp;#8217;s built-in &amp;#8220;makemigrations&amp;#8221; command might not output the most efficient
migration.&lt;/p&gt;
&lt;p&gt;This recipe show how one can optimize a migration Django creates.&lt;/p&gt;
&lt;p&gt;Take the following 2 models, a &amp;#8220;Book&amp;#8221; and a &amp;#8220;Library&amp;#8221;. Each book is associated
with exactly one library:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;library&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you run &lt;tt class="docutils literal"&gt;makemigrations&lt;/tt&gt; for an app with these models you get 3
operations.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The first one is a &lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt; operation and ensures the Book model
exists with a primary key and a name. But the reference to the library is
nowhere to be found.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt; operation for the Library looks as expected. There&amp;#8217;s a
primary key and a name.&lt;/li&gt;
&lt;li&gt;The next operation is an &lt;tt class="docutils literal"&gt;AddField&lt;/tt&gt;. And there it is, the library field for
the Book model.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;optimize_makemigrations&amp;#39;&lt;/span&gt;:
  0001_initial.py:
    - Create model Book
    - Create model Library
    - Add field library to book
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You may ask now why the &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; is added as a separate operation. There
are 2, maybe 3 reasons behind that:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Firstly, having references at the end of a migration when automatically
creating one is always safe.&lt;/li&gt;
&lt;li&gt;Secondly, Django&amp;#8217;s migration framework processes all models within the same
app in alphabetical order to provide a deterministic behavior. &amp;#8220;Book&amp;#8221; comes
before &amp;#8220;Library&amp;#8221;, last time I checked.&lt;/li&gt;
&lt;li&gt;Thirdly, nobody has contributed a patch yet. If you&amp;#8217;re staying for the
sprints, that might be something to look into.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;app.Library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, that we have this example, how can it be optimized? I guess some of you
already have an idea:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;app.Library&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The answer to that is, to re-order the &lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt; operations and merge the
&lt;tt class="docutils literal"&gt;AddField&lt;/tt&gt; into the &lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt; for the Book.&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/dont-be-afraid-of-writing-migrations"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/MarkusH/migration-recipes"&gt;Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category></entry><entry><title>Django Migrations Recipe #2</title><link href="https://markusholtermann.eu/2016/04/django-migrations-recipe-2/" rel="alternate"></link><published>2016-04-04T00:00:00+02:00</published><updated>2016-05-02T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-04-04:/2016/04/django-migrations-recipe-2/</id><summary type="html">&lt;p class="first last"&gt;This recipe shows how one can add a non-nullable column to a table
with already existing data.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is the second migration recipe I publish for Django&amp;#8217;s migration framework.
I published it as part of my &lt;a class="reference external" href="https://markusholtermann.eu/2016/04/dont-be-afraid-of-writing-migrations/"&gt;talk at DjangoCon Europe 2016 in Budapest&lt;/a&gt;. This recipe will show how to add a non-nullable column to a
table with already existing data.&lt;/p&gt;
&lt;p&gt;Adding columns is a common behavior when we write our software. Requirements
change and so do our models. Sometimes we&amp;#8217;d like to add a column that we don&amp;#8217;t
want to be nullable on database level.&lt;/p&gt;
&lt;p&gt;The following recipe works only when you have some data source where you can
get the value for existing values from. That could be a &lt;span class="caps"&gt;CSV&lt;/span&gt; file, an &lt;span class="caps"&gt;API&lt;/span&gt; that
you query or anything else.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s start with an Author model this time. The additional &amp;#8220;create&amp;#8221;
&lt;tt class="docutils literal"&gt;classmethod&lt;/tt&gt; is for demo purposes only and reflects all places in your code
where you create an Author instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before we add a field we need to create the migration for the current state.
We&amp;#8217;ll also create a migration for initial data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;:
  0001_initial.py:
    - Create model Author
$ python manage.py makemigrations non_nullable_field --empty --name initial_data
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;:
  0002_initial_data.py:
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We&amp;#8217;ll use the empty migration to create some initial data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema_editor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author 2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backwards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema_editor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author 2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunPython&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backwards&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each time this migration is applied the &amp;#8220;forwards&amp;#8221; function is being called and
two authors are being created. Each time the migration is unapplied the &amp;#8220;backwards&amp;#8221; function is being called
and the two authors are being deleted.&lt;/p&gt;
&lt;p&gt;We can then apply all migrations&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate non_nullable_field
Operations to perform:
  Apply all migrations: non_nullable_field
Running migrations:
  Rendering model states... DONE
  Applying non_nullable_field.0001_initial... OK
  Applying non_nullable_field.0002_initial_data... OK
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can proceed with adding an author&amp;#8217;s homepage. This is fairly easy by adding an URLField! Note that this is nullable for the
moment! We will change this to &lt;tt class="docutils literal"&gt;null=False&lt;/tt&gt; a bit later:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But first, let&amp;#8217;s create and run migrations! Again&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;:
  0003_author_homepage.py:
    - Add field homepage to author
$ python manage.py migrate non_nullable_field
Operations to perform:
  Apply all migrations: non_nullable_field
Running migrations:
  Rendering model states... DONE
  Applying non_nullable_field.0003_author_homepage... OK
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next step is taking care of the homepage field. Each time we are creating
an author we want to ensure the homepage is set. This applies to all new items.&lt;/p&gt;
&lt;p&gt;However, for existing rows we still need to take care of &lt;span class="caps"&gt;NULL&lt;/span&gt; values when
displaying the value:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;a href=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{u}&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{u}&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;i&amp;gt;No homepage&amp;lt;/i&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before we can eventually drop the so called &amp;#8220;&lt;span class="caps"&gt;NULL&lt;/span&gt; constraint&amp;#8221;, we need to
populate records with &lt;span class="caps"&gt;NULL&lt;/span&gt; for homepage in the database with values. We do that
in another migration. Go ahead and create an empty one&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations non_nullable_field --empty --name populate_data
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;:
  0004_populate_data.py:
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It doesn&amp;#8217;t really matter where you get the values for existing rows from. As
already said, this can be a &lt;span class="caps"&gt;CSV&lt;/span&gt; file, an &lt;span class="caps"&gt;API&lt;/span&gt; or anything else. The important
thing is that you ensure that in the end every row has a value. No row must be
&lt;span class="caps"&gt;NULL&lt;/span&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;

&lt;span class="n"&gt;LOOKUP_DATA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Author 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://example.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Author 2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://other.org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema_editor&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homepage__isnull&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOOKUP_DATA&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0003_homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunPython&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunPython&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;noop&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Go ahead and apply that migration. Everything past this comparably easy&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate non_nullable_field
Operations to perform:
  Apply all migrations: non_nullable_field
Running migrations:
  Rendering model states... DONE
  Applying non_nullable_field.0004_populate_data... OK
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s start by dropping the &amp;#8220;null=True&amp;#8221; from the URLField:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;a href=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{u}&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{u}&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;i&amp;gt;No homepage&amp;lt;/i&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you now run &amp;#8220;makemigrations&amp;#8221; Django is asking you how to handle the
change. Django doesn&amp;#8217;t know that we took care of all &lt;span class="caps"&gt;NULL&lt;/span&gt; values. Hence we need
to tell it: Select option 2&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations --name not_null_constraint

You are trying to change the nullable field to non-nullable without a default ...
Please &lt;span class="k"&gt;select&lt;/span&gt; a fix:
 &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; Provide a one-off value ...
 &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; Ignore &lt;span class="k"&gt;for&lt;/span&gt; now ...
 &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; Quit ...
Select an option: &lt;span class="m"&gt;2&lt;/span&gt;
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;:
  0005_not_null_constraint.py:
    - Alter field homepage on author
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the resulting migration. As you can see in the AlterField operation,
the URLField doesn&amp;#8217;t have a &lt;tt class="docutils literal"&gt;null=True&lt;/tt&gt; anymore and will therefore add a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;NOT&lt;/span&gt; &lt;span class="caps"&gt;NULL&lt;/span&gt;&lt;/tt&gt;
constraint to the database:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;non_nullable_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0004_populate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;homepage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLField&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Go ahead and apply that migration&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate non_nullable_field
Operations to perform:
  Apply all migrations: non_nullable_field
Running migrations:
  Rendering model states... DONE
  Applying non_nullable_field.0005_not_null_constraint... OK
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lastly, you should remove the code that handles &lt;span class="caps"&gt;NULL&lt;/span&gt; values from your code
base:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;homepage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;homepage_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;a href=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{u}&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{u}&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;/a&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/dont-be-afraid-of-writing-migrations"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/MarkusH/migration-recipes"&gt;Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category></entry><entry><title>Django Migrations Recipe #3</title><link href="https://markusholtermann.eu/2016/04/django-migrations-recipe-3/" rel="alternate"></link><published>2016-04-04T00:00:00+02:00</published><updated>2016-05-02T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-04-04:/2016/04/django-migrations-recipe-3/</id><summary type="html">&lt;p class="first last"&gt;This recipe shows how to rename an app that does not have any incoming
dependencies.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is the third migration recipe I publish for Django&amp;#8217;s migration framework.
I published it as part of my &lt;a class="reference external" href="https://markusholtermann.eu/2016/04/dont-be-afraid-of-writing-migrations/"&gt;talk at DjangoCon Europe 2016 in Budapest&lt;/a&gt;. This recipe will show how to rename an app. For simplicity
the app must not have any dependencies from other apps!&lt;/p&gt;
&lt;p&gt;Take an app &amp;#8220;rename_app&amp;#8221; with the following 2 models, an &amp;#8220;Author&amp;#8221; and a &amp;#8220;Book&amp;#8221;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# rename_app/models.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rename_app.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Similar to the previous recipe we create the initial migration and a data
migration and apply them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations
$ python manage.py makemigrations rename_app --empty --name initial_data

&lt;span class="c1"&gt;# Edit rename_app/migrations/0002_initial_data.py&lt;/span&gt;

$ python manage.py migrate rename_app
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first step in order to rename the app from &amp;#8220;rename_app&amp;#8221; to &amp;#8220;new_app_name&amp;#8221;
is forcing Django to a particular table name by adding the db_table attribute
on the models&amp;#8217; meta classes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# rename_app/models.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rename_app_author&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rename_app.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rename_app_book&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also create the respective migration for this change and apply it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations --name pin_db_tables
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# rename_app/migrations/0003_pin_db_tables.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rename_app&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0002_initial_data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterModelTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rename_app_author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterModelTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rename_app_book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next step is letting Django think you&amp;#8217;ve never applied these migrations
whilst not changing the database. You do that by passing the string &amp;#8220;zero&amp;#8221; as a
migration name together with the &amp;#8212;fake flag to the migrate management command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate rename_app zero --fake
Operations to perform:
  Unapply all migrations: rename_app
Running migrations:
  Rendering model states... DONE
  Unapplying rename_app.0003_pin_db_tables... FAKED
  Unapplying rename_app.0002_initial_data... FAKED
  Unapplying rename_app.0001_initial... FAKED
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that you need to do the actual rename of the files and references from
&amp;#8220;rename_app&amp;#8221; to &amp;#8220;new_app_name&amp;#8221; in every file, including your settings, models
and migration files.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;EXCEPT&lt;/span&gt; for the &amp;#8220;db_table&amp;#8221; attribute! &lt;span class="caps"&gt;DO&lt;/span&gt; &lt;span class="caps"&gt;NOT&lt;/span&gt; &lt;span class="caps"&gt;CHANGE&lt;/span&gt; &lt;span class="caps"&gt;THIS&lt;/span&gt; &lt;span class="caps"&gt;NOW&lt;/span&gt;!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# in settings.py&lt;/span&gt;
&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;new_app_name.apps.NewAppNameConfig&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="c1"&gt;# in new_app_name/models.py&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_app_name.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rename_app_book&amp;#39;&lt;/span&gt;  &lt;span class="c1"&gt;# Keep as is for now!&lt;/span&gt;


&lt;span class="c1"&gt;# in new_app_name/migrations/0003_pin_db_tables.py and others&lt;/span&gt;
&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_app_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0002_initial_data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you&amp;#8217;re done with that and you didn&amp;#8217;t make a mistake along the way, let
Django know about the new migrations. Again, you need to pass the &amp;#8212;fake flag
in order to only record the migration as applied and not do any database
operations:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate new_app_name --fake
Operations to perform:
  Apply all migrations: new_app_name
Running migrations:
  Rendering model states... DONE
  Applying new_app_name.0001_initial... FAKED
  Applying new_app_name.0002_initial_data... FAKED
  Applying new_app_name.0003_pin_db_tables... FAKED
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Last but not least, you can optionally drop the db_table attribute to rename
the tables from for example &amp;#8220;rename_app_author&amp;#8221; to &amp;#8220;new_app_name_author&amp;#8221;. When
you remove the db_table attribute you have to run makemigration again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# new_app_name/models.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_app_name.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you remove the db_table attribute you have to run makemigration again:&lt;/p&gt;
&lt;p&gt;What you can see here is, that the table name is reset to None. Django will
therefore automatically derive the name from the app name and model name.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations --name rename_tables
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# new_app_name/migrations/0004_rename_tables.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_app_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0003_pin_db_tables&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterModelTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlterModelTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/dont-be-afraid-of-writing-migrations"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/MarkusH/migration-recipes"&gt;Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category></entry><entry><title>Don’t be afraid of writing migrations</title><link href="https://markusholtermann.eu/2016/04/dont-be-afraid-of-writing-migrations/" rel="alternate"></link><published>2016-04-04T00:00:00+02:00</published><updated>2016-04-04T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-04-04:/2016/04/dont-be-afraid-of-writing-migrations/</id><summary type="html">&lt;p class="first last"&gt;Django migrations are complex. They do a lot for you but sometimes
you need to tell &amp;#8216;em how to do things. There&amp;#8217;s no reason to be afraid of
that, though.&lt;/p&gt;
</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/topics/migrations/"&gt;Django&amp;#8217;s migration framework&lt;/a&gt; evolved a lot
since it first release with 1.7.  Heaps of bugs have been fixed and numerous
features added.&lt;/p&gt;
&lt;p&gt;However, there are still lots of things that can be improved to make migrations
faster, some tasks more convenient to use, and some tasks even possible. One
thing in particular is the &lt;tt class="docutils literal"&gt;makemigrations&lt;/tt&gt; management command that
automatically outputs the required migrations when you changed a model. But
there are things &lt;tt class="docutils literal"&gt;makemigrations&lt;/tt&gt; just can&amp;#8217;t do. In those cases you need to
write migrations by hand.&lt;/p&gt;
&lt;p&gt;Writing migrations may sound scary, but in this blog post and the related talk
I want to explain why you don&amp;#8217;t need to be afraid of writing migrations. I
provide 3 recipes, an easy, and intermediate and a more complex one, which you
can use for to get a feeling for how migrations work and how to solve problems
you encounter in your own projects. Be advised that some of the recipes may
only work with PostgreSQL. SQLite doesn&amp;#8217;t enforce ForeignKey integrity and on
MySQL you&amp;#8217;re screwed when migrations fail. I didn&amp;#8217;t try Oracle.&lt;/p&gt;
&lt;h2&gt;General Layout&lt;/h2&gt;
&lt;p&gt;Before I go into any details or internals, let me explain the general layout of
a migration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You start off with a class called &lt;tt class="docutils literal"&gt;Migration&lt;/tt&gt; inheriting from
&lt;tt class="docutils literal"&gt;django.db.migrations.Migration&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;You then have an attribute &lt;tt class="docutils literal"&gt;dependencies&lt;/tt&gt; which is a list of 2-tuples
pointing to other migrations which this migration depends on. As an example,
when you have a &amp;#8220;Profile&amp;#8221; model with a &lt;tt class="docutils literal"&gt;ForeignKey&lt;/tt&gt; to a &amp;#8220;User&amp;#8221; model, the
user database table needs to exist before the profile table can point to the
user table.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;operations&amp;#8221; attribute holds a list of migration operations, such as
&lt;tt class="docutils literal"&gt;CreateModel&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;AddField&lt;/tt&gt;, or &lt;tt class="docutils literal"&gt;AlterField&lt;/tt&gt;. These operations will be run
in order when you apply a migration file.&lt;/p&gt;
&lt;h2&gt;Recipes&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2016/04/django-migrations-recipe-1/"&gt;Recipe #1 &amp;#8212; Optimize the initial creation of related models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2016/04/django-migrations-recipe-2/"&gt;Recipe #2 &amp;#8212; Add a non-nullable column to a table with already existing data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2016/04/django-migrations-recipe-3/"&gt;Recipe #3 &amp;#8212; Rename an app without incoming dependencies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/dont-be-afraid-of-writing-migrations"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/MarkusH/migration-recipes"&gt;Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Django"></category><category term="DjangoCon"></category><category term="Migration"></category><category term="Talk"></category></entry><entry><title>Syntax highlighting for Django’s SQL query logging</title><link href="https://markusholtermann.eu/2016/01/syntax-highlighting-for-djangos-sql-query-logging/" rel="alternate"></link><published>2016-01-30T00:00:00+01:00</published><updated>2016-05-16T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2016-01-30:/2016/01/syntax-highlighting-for-djangos-sql-query-logging/</id><summary type="html">&lt;p class="first last"&gt;Django provides a logger for all &lt;span class="caps"&gt;SQL&lt;/span&gt; queries you run and adds syntax
highlighting to the console output.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Let&amp;#8217;s start at the beginning: &lt;a class="reference external" href="https://docs.python.org/3/library/logging.html"&gt;Python&amp;#8217;s logging module&lt;/a&gt; is hard to understand.
And it&amp;#8217;s not much easier to configure it the way that you want it to behave.
There are &lt;tt class="docutils literal"&gt;filters&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;formatters&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;handlers&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;loggers&lt;/tt&gt;, to only name
a few components.&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;filters&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Provide a fine-grained way to suppress or allow a log record to be send to
&lt;tt class="docutils literal"&gt;handlers&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;formatters&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Given a formatting string and a log record, they generate the string that is
actually logged.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;handlers&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Define how a log record is handled. This could be an output to the console
or sending an email.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;loggers&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Tell the origin of a message on a higher level (stack, file, line number,
etc is derived automatically).&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This is a simple logging example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;primes.finder&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Next prime after &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; is &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The logger effectively has the name &lt;tt class="docutils literal"&gt;'primes.finder'&lt;/tt&gt; and will be referred to
by that name in Django&amp;#8217;s &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;LOGGING&lt;/span&gt;&lt;/tt&gt; configuration. Potentially defined filters
on a logger or handler would be consulted before a message is passed to a
handler from the logger. Such a filter could e.g. inspect the two primes and
see if the second has more digits than the first.&lt;/p&gt;
&lt;p&gt;A handler takes care of outputting a log record. It could e.g. output the log
message on a command line or send it via email. In the former case a formatter
would be used to define a general format of the log messages, e.g. prepend a
timestamp and log level.&lt;/p&gt;
&lt;p&gt;In order to provide syntax highlighting for the &lt;span class="caps"&gt;SQL&lt;/span&gt; queries Django runs we will
need to write our own formatter. To do that, we need to look at the way Django
logs the queries (&lt;tt class="docutils literal"&gt;django.db.backends.utils.CursorDebugWrapper.execute()&lt;/tt&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;(&lt;/span&gt;&lt;span class="si"&gt;%.3f&lt;/span&gt;&lt;span class="s1"&gt;) &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;; args=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;duration&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;params&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What we can see here is the &lt;tt class="docutils literal"&gt;record&lt;/tt&gt; object that our the formatter will
receive has the attributes &lt;tt class="docutils literal"&gt;duration&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;sql&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;params&lt;/tt&gt;, derived from
the &lt;tt class="docutils literal"&gt;extra&lt;/tt&gt; keyword argument.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQLFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Check if Pygments is available for coloring&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pygments&lt;/span&gt;
            &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pygments.lexers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SqlLexer&lt;/span&gt;
            &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pygments.formatters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TerminalTrueColorFormatter&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pygments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

        &lt;span class="c1"&gt;# Check if sqlparse is available for indentation&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sqlparse&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sqlparse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

        &lt;span class="c1"&gt;# Remove leading and trailing whitespaces&lt;/span&gt;
        &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Indent the SQL query&lt;/span&gt;
            &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reindent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pygments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Highlight the SQL query&lt;/span&gt;
            &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pygments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;SqlLexer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;TerminalTrueColorFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;monokai&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Set the record&amp;#39;s statement to the formatted query&lt;/span&gt;
        &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLFormatter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Update your &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;LOGGING&lt;/span&gt;&lt;/tt&gt; configuration to include the &lt;tt class="docutils literal"&gt;sql&lt;/tt&gt; formatter, &lt;tt class="docutils literal"&gt;sql&lt;/tt&gt;
handler and &lt;tt class="docutils literal"&gt;django.db.backends&lt;/tt&gt; logger:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;LOGGING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;disable_existing_loggers&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;formatters&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;()&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;path.to.your.SQLFormatter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;format&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[&lt;/span&gt;&lt;span class="si"&gt;%(duration).3f&lt;/span&gt;&lt;span class="s1"&gt;] &lt;/span&gt;&lt;span class="si"&gt;%(statement)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;handlers&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;console&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;DEBUG&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;logging.StreamHandler&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;logging.StreamHandler&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;formatter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;DEBUG&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;loggers&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;django.db.backends&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;handlers&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sql&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;DEBUG&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;propagate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;django.db.backends.schema&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;handlers&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;console&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;level&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;DEBUG&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;propagate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: In case you&amp;#8217;re working with a 256 color terminal, you should use
the &lt;tt class="docutils literal"&gt;Terminal256Formatter&lt;/tt&gt; instead of the &lt;tt class="docutils literal"&gt;TerminalTrueColorFormatter&lt;/tt&gt; in
the &lt;tt class="docutils literal"&gt;SQLFormatter.format()&lt;/tt&gt; method. (Thanks &lt;a class="reference external" href="https://twitter.com/felixhummel/status/730684858738053120"&gt;Felix Hummel&lt;/a&gt;).&lt;/p&gt;
</content><category term="Development"></category><category term="Database"></category><category term="Django"></category><category term="Logging"></category><category term="SQL"></category></entry><entry><title>Update: The Necessity of Configuration and System Management Tools</title><link href="https://markusholtermann.eu/2015/08/the-necessity-of-configuration-and-system-management-tools/" rel="alternate"></link><published>2015-08-01T00:00:00+02:00</published><updated>2015-08-04T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2015-08-01:/2015/08/the-necessity-of-configuration-and-system-management-tools/</id><summary type="html">&lt;p class="first last"&gt;Over the last months I wrote my Master&amp;#8217;s Thesis which I&amp;#8217;m publishing
with this post.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Over the last months I wrote my Master&amp;#8217;s Thesis in Computer Science about
configuration and system management tools at the &lt;a class="reference external" href="http://www.tu-berlin.de"&gt;Technical University of
Berlin, Germany&lt;/a&gt;, supervised by &lt;a class="reference external" href="http://www.cit.tu-berlin.de/kao/parameter/en/"&gt;Prof. Dr. Odej Kao&lt;/a&gt;. I was also supported by
&lt;a class="reference external" href="http://flyingcircus.io"&gt;Flying Circus Internet Operations GmbH&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The official title of my thesis is &amp;#8220;&lt;em&gt;Evaluating Methods to Maintain System
Stability and Security When Reversing Changes Made by Configuration and System
Management Tools in &lt;span class="caps"&gt;UNIX&lt;/span&gt; Environments&lt;/em&gt;&amp;#8220;. In essence it&amp;#8217;s points out what you as
a system administrator should care about and take into account when using
configuration and system management tools, such as &lt;a class="reference external" href="http://ansible.com"&gt;Ansible&lt;/a&gt;, &lt;a class="reference external" href="https://www.getchef.com"&gt;Chef&lt;/a&gt;, and
&lt;a class="reference external" href="https://docs.puppetlabs.com"&gt;Puppet&lt;/a&gt;. Using these tools is easy, and takes a lot of your plate when
dealing with larger &lt;span class="caps"&gt;IT&lt;/span&gt; environments, but without considering certain things you
likely break your environment at some point.&lt;/p&gt;
&lt;p&gt;Consider the following example:&lt;/p&gt;
&lt;blockquote&gt;
A hosting provider offers virtual machines that have an &lt;span class="caps"&gt;IP&lt;/span&gt; address that is
assigned from a pool of free addresses when the machine is first set up. At
some point a customer requests the server to be shut down and removed. Once
the server is removed, its &lt;span class="caps"&gt;IP&lt;/span&gt; address is freed and goes back into the pool.
However, a customer is able to request a restore of a server from a backup
within a given time frame after its removal. This works perfectly fine as
long as the original &lt;span class="caps"&gt;IP&lt;/span&gt; address is not in use. However, configuring the
network setup will fail if the &lt;span class="caps"&gt;IP&lt;/span&gt; address has been recycled to a different
machine. [ch. 1, p. 1]&lt;/blockquote&gt;
&lt;p&gt;My master&amp;#8217;s thesis deals with this and many other cases where problems might be
hard to spot.&lt;/p&gt;
&lt;p&gt;In order for you to inspect your &lt;span class="caps"&gt;IT&lt;/span&gt; environment and find those problems I came
up with a taxonomy of &lt;span class="caps"&gt;IT&lt;/span&gt; resources that helps you classify all resources
[ch. 5.1, p. 23]:&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m12 l12" href="/images/mastersthesis/taxonomy.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/mastersthesis/taxonomy-987x987.jpg" alt="classification of IT resources." title="classification of IT resources."&gt;&lt;img class="medium lazyload" data-src="/images/thumb/mastersthesis/taxonomy-748x748.jpg" alt="classification of IT resources." title="classification of IT resources."&gt;&lt;img class="small lazyload" data-src="/images/thumb/mastersthesis/taxonomy-480x480.jpg" alt="classification of IT resources." title="classification of IT resources."&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;For the example given above, the &lt;span class="caps"&gt;IP&lt;/span&gt; addresses of VMs are identifying resources.&lt;/p&gt;
&lt;p&gt;After classifying your resources you can follow the rules I derived from the
taxonomy ([ch. 5.2, p. 26]) to narrow down potential conflicts. In the above
case &amp;#8220;&lt;em&gt;Rule 6: Use environment-wide unique identifiers&lt;/em&gt;&amp;#8221; is violated. The
hosting provider should keep a record of &lt;span class="caps"&gt;IP&lt;/span&gt; addresses that are still in use
inside backups before actually recycling the addresses.&lt;/p&gt;
&lt;p&gt;In chapter 5.3 I provide a variety of use cases that you might encounter in
your &lt;span class="caps"&gt;IT&lt;/span&gt; environment. Chapter 6 takes up those cases and outlines possibilities
of how to solve potential issues.&lt;/p&gt;
&lt;h2&gt;License&lt;/h2&gt;
&lt;p&gt;You are allowed to use all resources that are part of this article free of
charge as long as you include my name and a reference to this article in a
place the end-user of your product sees.&lt;/p&gt;
&lt;p&gt;You may not copy, distribute, or publish the thesis as a whole unless
explicitly granted.&lt;/p&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/files/masterthesis-v1.1.pdf"&gt;Master&amp;#8217;s Thesis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/the-necessity-of-configuration-and-system-management-tools"&gt;Presentation from PyCon Australia 2015&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=1NowxI9WATs"&gt;Video recording from PyCon Australia 2015&lt;/a&gt; &amp;#8212; I have to admit I was quite
nervous&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Ansible"></category><category term="Chef"></category><category term="Linux"></category><category term="Network"></category><category term="Puppet"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>DjangoCon Europe 2015 — The Boy Band Conference</title><link href="https://markusholtermann.eu/2015/06/djangocon-europe-2015/" rel="alternate"></link><published>2015-06-15T00:00:00+02:00</published><updated>2015-06-15T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2015-06-15:/2015/06/djangocon-europe-2015/</id><summary type="html">&lt;p class="first last"&gt;2015&amp;#8217;s DjangoCon Europe edition with 350 attendees took place in the
beautiful city of Cardiff, Wales, &lt;span class="caps"&gt;UK&lt;/span&gt;.&lt;/p&gt;
</summary><content type="html">&lt;h2&gt;The Announcement&lt;/h2&gt;
&lt;p&gt;The announcement for &lt;a class="reference external" href="http://2015.djangocon.eu/"&gt;DjangoCon Europe 2015&lt;/a&gt; &amp;#8212; as the conference itself &amp;#8212;
was vast. A six day conference, including an open day, open and free to the
public, three days of &amp;#8220;conference talks&amp;#8221;, two days of code sprints, workshops
and code clinics. Furthermore, yet another &lt;a class="reference external" href="http://djangogirls.org/"&gt;DjangoGirls&lt;/a&gt; event. The organizers
also took up the cause of making the most diverse, accessible and social
responsible DjangoCon ever. And, from my perspective, they succeeded! The team
around &lt;a class="reference external" href="https://twitter.com/evildmp"&gt;Daniele Procida&lt;/a&gt; organized a memorable conference that around 350
people attended. Thank You!&lt;/p&gt;
&lt;h2&gt;My Trip to Cardiff&lt;/h2&gt;
&lt;p&gt;My trip to Cardiff wasn&amp;#8217;t my first time in the &lt;span class="caps"&gt;UK&lt;/span&gt;, but it was the longest. All
the times I have be to &lt;span class="caps"&gt;UK&lt;/span&gt; before, were shorter. And all these other trips were
way rainier that this time.&lt;/p&gt;
&lt;p&gt;My plane left Berlin Tegel Airport (&lt;span class="caps"&gt;TXL&lt;/span&gt;) on May 30th at 6am, quite early in the
morning. On the plus side, I already met &lt;a class="reference external" href="https://twitter.com/erikpub"&gt;Erik Romijn&lt;/a&gt;, another Django
committer, at Amsterdam Schiphol Airport (&lt;span class="caps"&gt;AMS&lt;/span&gt;) for our flight to Cardiff
Airport (&lt;span class="caps"&gt;CWL&lt;/span&gt;). Interestingly, our airline, &lt;span class="caps"&gt;KLM&lt;/span&gt;, overbooked the flight and was
offering 250 &lt;span class="caps"&gt;EUR&lt;/span&gt; to the first person that would take a later flight.&lt;/p&gt;
&lt;p&gt;On both, the flight from &lt;span class="caps"&gt;TXL&lt;/span&gt; to &lt;span class="caps"&gt;AMS&lt;/span&gt; and after the conference from &lt;span class="caps"&gt;CWL&lt;/span&gt; to &lt;span class="caps"&gt;AMS&lt;/span&gt;,
&lt;span class="caps"&gt;KLM&lt;/span&gt; served sandwiches. And these sandwiches perfectly aligned with one of the
policies of the conference: social responsibility.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce01.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce01-483x483.jpg" alt="KLM sandwich box on flight from TXL to AMS with note 'The chicken /
egg used on this bread are raised and produced with respect for animal
welfare.'" title="KLM sandwich box on flight from TXL to AMS with note 'The chicken /
egg used on this bread are raised and produced with respect for animal
welfare.'"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce01-364x364.jpg" alt="KLM sandwich box on flight from TXL to AMS with note 'The chicken /
egg used on this bread are raised and produced with respect for animal
welfare.'" title="KLM sandwich box on flight from TXL to AMS with note 'The chicken /
egg used on this bread are raised and produced with respect for animal
welfare.'"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce01-480x480.jpg" alt="KLM sandwich box on flight from TXL to AMS with note 'The chicken /
egg used on this bread are raised and produced with respect for animal
welfare.'" title="KLM sandwich box on flight from TXL to AMS with note 'The chicken /
egg used on this bread are raised and produced with respect for animal
welfare.'"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce02.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce02-483x483.jpg" alt="KLM sandwich box on flight from CWL to AMS with note 'The premium
cheese used on this bread is made of tasty milk from happy cows.'" title="KLM sandwich box on flight from CWL to AMS with note 'The premium
cheese used on this bread is made of tasty milk from happy cows.'"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce02-364x364.jpg" alt="KLM sandwich box on flight from CWL to AMS with note 'The premium
cheese used on this bread is made of tasty milk from happy cows.'" title="KLM sandwich box on flight from CWL to AMS with note 'The premium
cheese used on this bread is made of tasty milk from happy cows.'"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce02-480x480.jpg" alt="KLM sandwich box on flight from CWL to AMS with note 'The premium
cheese used on this bread is made of tasty milk from happy cows.'" title="KLM sandwich box on flight from CWL to AMS with note 'The premium
cheese used on this bread is made of tasty milk from happy cows.'"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;Cardiff&amp;#8217;s Architecture&lt;/h2&gt;
&lt;p&gt;The conference and all related events took place at &lt;a class="reference external" href="https://twitter.com/cardiffuni"&gt;Cardiff University&lt;/a&gt; (a
huge thank you for being involved!) and Cardiff City Hall.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce03.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce03-315x315.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce03-236x236.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce03-480x480.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce04.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce04-315x315.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce04-236x236.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce04-480x480.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce05.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce05-315x315.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce05-236x236.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce05-480x480.jpg" alt="Cardiff University main hall" title="Cardiff University main hall"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce06.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce06-315x315.jpg" alt="Cardiff University registration room" title="Cardiff University registration room"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce06-236x236.jpg" alt="Cardiff University registration room" title="Cardiff University registration room"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce06-480x480.jpg" alt="Cardiff University registration room" title="Cardiff University registration room"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce07.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce07-315x315.jpg" alt="Cardiff City Hall" title="Cardiff City Hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce07-236x236.jpg" alt="Cardiff City Hall" title="Cardiff City Hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce07-480x480.jpg" alt="Cardiff City Hall" title="Cardiff City Hall"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce08.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce08-315x315.jpg" alt="Cardiff City Hall -- Daniele Procida opening the conference" title="Cardiff City Hall -- Daniele Procida opening the conference"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce08-236x236.jpg" alt="Cardiff City Hall -- Daniele Procida opening the conference" title="Cardiff City Hall -- Daniele Procida opening the conference"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce08-480x480.jpg" alt="Cardiff City Hall -- Daniele Procida opening the conference" title="Cardiff City Hall -- Daniele Procida opening the conference"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h3&gt;DjangoGirls&lt;/h3&gt;
&lt;p&gt;Especially Cardiff University conveys a sense of good old British university
life. The stunning building, with its many small corridors and corners, served
well for the DjangoGirls workshop, putting everybody back into (or forward to)
their university life.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce09.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce09-483x483.jpg" alt="DjangoGirls attendees" title="DjangoGirls attendees"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce09-364x364.jpg" alt="DjangoGirls attendees" title="DjangoGirls attendees"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce09-480x480.jpg" alt="DjangoGirls attendees" title="DjangoGirls attendees"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce10.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce10-483x483.jpg" alt="DjangoGirls attendees" title="DjangoGirls attendees"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce10-364x364.jpg" alt="DjangoGirls attendees" title="DjangoGirls attendees"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce10-480x480.jpg" alt="DjangoGirls attendees" title="DjangoGirls attendees"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce11.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce11-483x483.jpg" alt="DjangoGirls fruits" title="DjangoGirls fruits"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce11-364x364.jpg" alt="DjangoGirls fruits" title="DjangoGirls fruits"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce11-480x480.jpg" alt="DjangoGirls fruits" title="DjangoGirls fruits"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce12.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce12-483x483.jpg" alt="DjangoGirls fruits" title="DjangoGirls fruits"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce12-364x364.jpg" alt="DjangoGirls fruits" title="DjangoGirls fruits"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce12-480x480.jpg" alt="DjangoGirls fruits" title="DjangoGirls fruits"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h3&gt;Conference Dinner&lt;/h3&gt;
&lt;p&gt;Right next to Cardiff City Hall, the National Museum Cardiff was host for the
conference dinner. I can&amp;#8217;t really remember what the meal was called, but I
know, it was really good and tasty.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce13.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce13-315x315.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce13-236x236.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce13-480x480.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce14.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce14-315x315.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce14-236x236.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce14-480x480.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce15.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce15-315x315.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce15-236x236.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce15-480x480.jpg" alt="National Museum Cardiff -- Entrance hall" title="National Museum Cardiff -- Entrance hall"&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce16.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce16-483x483.jpg" alt="Conference dinner -- Main course" title="Conference dinner -- Main course"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce16-364x364.jpg" alt="Conference dinner -- Main course" title="Conference dinner -- Main course"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce16-480x480.jpg" alt="Conference dinner -- Main course" title="Conference dinner -- Main course"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce17.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce17-483x483.jpg" alt="Conference dinner -- Dessert" title="Conference dinner -- Dessert"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce17-364x364.jpg" alt="Conference dinner -- Dessert" title="Conference dinner -- Dessert"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce17-480x480.jpg" alt="Conference dinner -- Dessert" title="Conference dinner -- Dessert"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h3&gt;Cardiff Castle&lt;/h3&gt;
&lt;p&gt;Cardiff&amp;#8217;s landmark is its castle (Welsh: Castell Caerdydd). It &amp;#8220;was built in
the late 11th century by Norman invaders on top of a 3rd-century Roman fort.&amp;#8221;
&lt;a class="citation-reference" href="#wiki" id="id1"&gt;[Wiki]&lt;/a&gt;&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce18.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce18-483x483.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce18-364x364.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce18-480x480.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce19.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce19-483x483.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce19-364x364.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce19-480x480.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce20.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce20-483x483.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce20-364x364.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce20-480x480.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce21.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce21-483x483.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce21-364x364.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce21-480x480.jpg" alt="Cardiff Castle" title="Cardiff Castle"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;The Conference&lt;/h2&gt;
&lt;h3&gt;Talks&lt;/h3&gt;
&lt;p&gt;The conference itself was great. The organizers encouraged people that hadn&amp;#8217;t
been on stage to give a presentation in front of hundreds of people. And it
worked quite good. I&amp;#8217;ve seen many first time speakers and I was one myself,
talking about &amp;#8220;&lt;a class="reference external" href="https://speakerdeck.com/markush/forms-are-static-no-they-arent"&gt;Forms are static &amp;#8212; No, they aren&amp;#8217;t&lt;/a&gt;&amp;#8221; and my first Django
third-party app &lt;a class="reference external" href="https://github.com/MarkusH/django-dynamic-forms"&gt;django-dynamic-forms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://speakerdeck.com/bmispelon/baptistes-adventures-in-djangoland"&gt;opening keynote&lt;/a&gt; by &lt;a class="reference external" href="https://twitter.com/bmispelon"&gt;Baptiste Mispelon&lt;/a&gt; was a story of &amp;#8220;how he tackled
burnout by traveling around Europe, eating Welsh cakes, pierogi and
stroopwafel&amp;#8221; &lt;a class="citation-reference" href="#dce15" id="id2"&gt;[&lt;span class="caps"&gt;DCE15&lt;/span&gt;]&lt;/a&gt;. The entire story made out of Emoji, his favorite
language. Nothing even close to programming or code, but an insightful and
honest statement of a common problem in &lt;span class="caps"&gt;IT&lt;/span&gt;.&lt;/p&gt;
&lt;div class="speakerdeck"&gt;&lt;script async class="speakerdeck-embed" data-id="9fdb5f5c51e14362aa3e993af3ee0b96" data-ratio="1.3333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;/div&gt;&lt;p&gt;This topic, to listen to yourself and to your friends and colleagues around
you, was also supported by the wellbeing service of Cardiff University that
offered free sessions for everybody who wanted to talk to somebody and get some
recommendations or ideas.but&lt;/p&gt;
&lt;p&gt;But even more surprising &amp;#8212; at least to me and likely many others in the
audience &amp;#8212; was &lt;a class="reference external" href="https://twitter.com/freakboy3742"&gt;Russell Keith-Magee&amp;#8217;s&lt;/a&gt; lightning about his illness. Thank you
for being so open, trusting us with your story and get well soon :)&lt;/p&gt;
&lt;p&gt;Another talk I specifically want to mention here, is the keynote from the
second day by &lt;a class="reference external" href="https://twitter.com/asendecka"&gt;Ola Sendecka&lt;/a&gt;: &lt;a class="reference external" href="https://speakerdeck.com/asendecka/into-the-rabbit-hole"&gt;Into the rabbit hole&lt;/a&gt;, about a girl Alice in
Djangoland. But the most impressive about this talk was not the content (that
was great and correct), but the hand painted images.&lt;/p&gt;
&lt;div class="speakerdeck"&gt;&lt;script async class="speakerdeck-embed" data-id="6b472deb9c5441b1be2491e2fd2de2bb" data-ratio="1.41241379310345" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;/div&gt;&lt;p&gt;There were plenty of other talks I enjoyed a lot, because they explained
something difficult in a simple way (&lt;a class="reference external" href="https://twitter.com/anabalica"&gt;Ana Balica&lt;/a&gt;: &lt;a class="reference external" href="http://www.slideshare.net/AnaBalica/djangocon2015-demystifying-mixins-with-django"&gt;Demystifying mixins with
Django&lt;/a&gt;), because they showed something outside Django&amp;#8217;s normal use case
(Dafydd Evans: &lt;span class="caps"&gt;CAMEL&lt;/span&gt;, the Cardiff Maths e-learning project), or because they
showed once more that computer science, and computer security in particular, is
hard (&lt;a class="reference external" href="https://twitter.com/ubernostrum"&gt;James Bennett&lt;/a&gt;: The net is dark and full of terrors).&lt;/p&gt;
&lt;h3&gt;Accessibility&lt;/h3&gt;
&lt;p&gt;As already announced, the organizers wanted everybody to feel welcome. And an
important support for people with hearing disabilities were the live
transcripts by &lt;a class="reference external" href="https://twitter.com/STTRWales"&gt;Sheryll Holley&lt;/a&gt; and Hilary Maclean, that are now &lt;a class="reference external" href="https://github.com/evildmp/DjangoConEuropeTranscripts"&gt;public&lt;/a&gt; for
everybody to read.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce22.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce22-483x483.jpg" alt="Live speech-to-text reporters Sheryll Holley and Hilary Maclean" title="Live speech-to-text reporters Sheryll Holley and Hilary Maclean"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce22-364x364.jpg" alt="Live speech-to-text reporters Sheryll Holley and Hilary Maclean" title="Live speech-to-text reporters Sheryll Holley and Hilary Maclean"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce22-480x480.jpg" alt="Live speech-to-text reporters Sheryll Holley and Hilary Maclean" title="Live speech-to-text reporters Sheryll Holley and Hilary Maclean"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/djangoconeu2015/dce23.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce23-483x483.jpg" alt="Live speech-to-text monitor" title="Live speech-to-text monitor"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce23-364x364.jpg" alt="Live speech-to-text monitor" title="Live speech-to-text monitor"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce23-480x480.jpg" alt="Live speech-to-text monitor" title="Live speech-to-text monitor"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;One Direction&lt;/h2&gt;
&lt;p&gt;The most important part of the entire DjangoCon, though, were the two One
Direction concerts on Friday and Saturday evening after the conference. About
110 000 fans and visitors were expected. That is a third of the population of
Cardiff:&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce24.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce24-315x315.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce24-236x236.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce24-480x480.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce25.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce25-315x315.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce25-236x236.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce25-480x480.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2015/dce26.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2015/dce26-315x315.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2015/dce26-236x236.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2015/dce26-480x480.jpg" alt="One Direction fans on their way to Cardiff stadium" title="One Direction fans on their way to Cardiff stadium"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;License&lt;/h2&gt;
&lt;div class="figure align-right"&gt;
&lt;a class="reference external image-reference" href="http://creativecommons.org/licenses/by-sa/4.0/"&gt;&lt;img alt="Creative Commons License" src="/images/cc-by-sa-3.0-88x31.png" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;The photos in this blog post are licensed under a &lt;a class="reference external" href="http://creativecommons.org/licenses/by-sa/4.0/"&gt;Creative Commons
Attribution-ShareAlike 4.0 International License&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The article image (group photo) is licensed under the terms of the
&lt;a class="reference external" href="https://creativecommons.org/licenses/by-sa/2.0/"&gt;Creative Commons Attribution-ShareAlike 2.0 Generic (&lt;span class="caps"&gt;CC&lt;/span&gt; &lt;span class="caps"&gt;BY&lt;/span&gt;-&lt;span class="caps"&gt;SA&lt;/span&gt; 2.0)&lt;/a&gt; by &lt;a class="reference external" href="https://www.flickr.com/photos/patrys/18510465600/"&gt;Patryk Zawadzki&lt;/a&gt;.&lt;/p&gt;
&lt;table class="docutils citation" frame="void" id="wiki" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[Wiki]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cardiff_Castle"&gt;https://en.wikipedia.org/wiki/Cardiff_Castle&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="dce15" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id2"&gt;[&lt;span class="caps"&gt;DCE15&lt;/span&gt;]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://2015.djangocon.eu/talks/"&gt;http://2015.djangocon.eu/talks/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Misc"></category><category term="Community"></category><category term="Django"></category><category term="DjangoCon"></category></entry><entry><title>A lot has happended!</title><link href="https://markusholtermann.eu/2015/04/a-lot-has-happened/" rel="alternate"></link><published>2015-04-11T00:00:00+02:00</published><updated>2015-04-11T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2015-04-11:/2015/04/a-lot-has-happened/</id><summary type="html">&lt;p class="first last"&gt;It has been 5 months that I wrote something on my blog. I still
remember the time writing the article. On my way to Django: Under The Hood
2014. And shortly after that my time to write something here vanished.
For various reasons.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;It has been 5 months that I wrote something on my blog. I still remember the
time writing the article. On my way to &lt;a class="reference external" href="http://www.djangounderthehood.com/"&gt;Django: Under The Hood 2014&lt;/a&gt;, my
favorite conference in 2014 as an attendee. And shortly after that my time to
write something here vanished. For various reasons.&lt;/p&gt;
&lt;h2&gt;December 2014&lt;/h2&gt;
&lt;p&gt;While getting &lt;a class="reference external" href="https://github.com/django/django/graphs/contributors?from=2014-12-01&amp;amp;to=2015-01-01&amp;amp;type=c"&gt;9 commits&lt;/a&gt; into Django&amp;#8217;s code base throughout December, we (the
server team at &lt;a class="reference external" href="http://ubuntuusers.de"&gt;ubuntuusers.de&lt;/a&gt;) finally &lt;a class="reference external" href="https://ubuntuusers.statuspage.io/incidents/mb0wt1jnhg3s"&gt;upgraded our infrastructure&lt;/a&gt; to new
hardware. A few days before the holidays I decided to finally fill out the form
and enrolled for my master thesis.&lt;/p&gt;
&lt;h2&gt;January 2015&lt;/h2&gt;
&lt;p&gt;2015 began quietly. Not many things happened. Until January 9th! &lt;a class="reference external" href="https://github.com/carljm"&gt;Carl Meyer&lt;/a&gt;
queried me on &lt;span class="caps"&gt;IRC&lt;/span&gt;:&lt;/p&gt;
&lt;blockquote&gt;
We&amp;#8217;d like to invite you to be part of the Django core team, if you&amp;#8217;re
interested.&lt;/blockquote&gt;
&lt;p&gt;I was super happy and excited (and still am). And according to the &lt;a class="reference external" href="https://www.djangoproject.com/weblog/2015/jan/11/new-core-team-members/"&gt;official
announcement&lt;/a&gt; I was &lt;em&gt;the most active non-core-team code contributor&lt;/em&gt; in
2014. Interesting, I wasn&amp;#8217;t aware of that and haven&amp;#8217;t even though about it :)&lt;/p&gt;
&lt;p&gt;Later that month I got the confirmation for the topic of my Master Thesis. I
won&amp;#8217;t tell it here until I finished and got my grade for it. It&amp;#8217;s about
configuration and system management tools. But I&amp;#8217;m working on my thesis ever
since which is, together with my work as Django core developer, the reason why
I don&amp;#8217;t really have time to do anything else.&lt;/p&gt;
&lt;p&gt;On 25th I was a coach at &lt;a class="reference external" href="http://djangogirls.org/berlin/"&gt;DjangoGirls Berlin&lt;/a&gt;. That&amp;#8217;s the second time I&amp;#8217;ve
been coaching (first time was after Django: Under The Hood) and the second time
DjangoGirls came to Berlin (first time was at &lt;a class="reference external" href="https://ep2014.europython.eu/en/conference/satellite-events/django-girls-workshop/"&gt;EuroPython 2014&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;February 2015&lt;/h2&gt;
&lt;p&gt;There are not many things worth mentioning for February. I gave a lightning
talk at the &lt;a class="reference external" href="http://www.meetup.com/Python-Users-Berlin-PUB/events/219427342/"&gt;Python Users Berlin meetup&lt;/a&gt; about how to integrate &lt;a class="reference external" href="http://sphinx-doc.org/"&gt;Sphinx&lt;/a&gt; and
&lt;a class="reference external" href="https://www.elastic.co/products/elasticsearch"&gt;Elasticsearch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A week later I gave a talk at the &lt;a class="reference external" href="http://www.meetup.com/django-user-group-berlin/events/219547330/"&gt;Django User Group Berlin meetup&lt;/a&gt; about
&lt;em&gt;Combining Django &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; Elasticsearch&lt;/em&gt; (&lt;a class="reference external" href="https://speakerdeck.com/markush/combining-django-and-elasticsearch"&gt;Slides&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;March 2015&lt;/h2&gt;
&lt;p&gt;Compared to February, March was really busy in terms of travelling. I went to
the &lt;a class="reference external" href="http://www.meetup.com/dutch-django-assocation/events/220368460/"&gt;Django Sprint in Amsterdam&lt;/a&gt;. Thanks again to the organizers. It was a
pleasure to be back in Amsterdam.&lt;/p&gt;
&lt;p&gt;One week later a couple of friends and I finally went on a skiing vacation
again. We had amazing weather. The view from the top of the mountains was
breathtaking. &lt;a class="reference external" href="https://plus.google.com/+MarkusHoltermann/posts/h2CiMHpdtRC"&gt;I took a few photos&lt;/a&gt;! The photos are licensed under a &lt;a class="reference external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;Creative
Commons Attribution-NonCommercial-ShareAlike 4.0 International License&lt;/a&gt;
(&lt;span class="caps"&gt;CC&lt;/span&gt;-&lt;span class="caps"&gt;BY&lt;/span&gt;-&lt;span class="caps"&gt;NC&lt;/span&gt;-&lt;span class="caps"&gt;SA&lt;/span&gt;).&lt;/p&gt;
&lt;h2&gt;April 2015&lt;/h2&gt;
&lt;p&gt;On April 1st we released &lt;a class="reference external" href="https://www.djangoproject.com/weblog/2015/apr/01/release-18-final/"&gt;Django 1.8&lt;/a&gt; as our next &lt;span class="caps"&gt;LTS&lt;/span&gt; (Long Term Support)
version with a hole bunch of new features and improvements:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Native support for multiple template engines.&lt;/li&gt;
&lt;li&gt;Support for complex &lt;span class="caps"&gt;SQL&lt;/span&gt; expressions via the &lt;span class="caps"&gt;ORM&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;A formalized &lt;span class="caps"&gt;API&lt;/span&gt; for &lt;tt class="docutils literal"&gt;Model._meta&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;New PostgreSQL specific functionality in &lt;tt class="docutils literal"&gt;contrib.postgres&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The features and changes I&amp;#8217;m particularly happy about are the serialization of
model managers in migrations (&lt;a class="reference external" href="https://code.djangoproject.com/ticket/23822"&gt;#23822&lt;/a&gt;) which I have worked on for quite a
while and of course the performance improvements in migrations. It was some
hard work but thanks to the amazing work &lt;a class="reference external" href="https://github.com/claudep"&gt;Claude Paroz&lt;/a&gt; did on &lt;a class="reference external" href="https://code.djangoproject.com/ticket/23745"&gt;#23745&lt;/a&gt; and
&lt;a class="reference external" href="https://github.com/knbk"&gt;Marten Kenbeek&lt;/a&gt; did on &lt;a class="reference external" href="https://code.djangoproject.com/ticket/24366"&gt;#24366&lt;/a&gt; the migrations in 1.8 are noticeable faster.
Of course thanks to all contributors who made the release the way it is :)&lt;/p&gt;
</content><category term="Misc"></category><category term="Personal"></category><category term="Django"></category><category term="Studium"></category></entry><entry><title>Flashing Android 5 “Lollipop”: Lessons Learned</title><link href="https://markusholtermann.eu/2014/11/flashing-android-5-lollipop-lessons-learned/" rel="alternate"></link><published>2014-11-13T00:00:00+01:00</published><updated>2014-11-13T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-11-13:/2014/11/flashing-android-5-lollipop-lessons-learned/</id><summary type="html">&lt;p class="first last"&gt;Yesterday evening Google finally published the Android 5 &amp;#8220;Lollipop&amp;#8221;
factory images. Eagerly waiting for the update I couldn&amp;#8217;t hold myself and
flashed my Nexus 5. This was the first time on the Nexus 5, the previous
KitKat updates came &lt;span class="caps"&gt;OTA&lt;/span&gt;. Though I&amp;#8217;m not really familiar with the whole
flashing thing, the entire process went smoothly. At least kind of.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Yesterday evening Google finally published the &lt;a class="reference external" href="https://developers.google.com/android/nexus/images"&gt;Android 5 &amp;#8220;Lollipop&amp;#8221; factory
images&lt;/a&gt;. Eagerly waiting for the update I couldn&amp;#8217;t hold myself and flashed my
Nexus 5. This was the first time on the Nexus 5, the previous KitKat updates
came &lt;span class="caps"&gt;OTA&lt;/span&gt;. Though I&amp;#8217;m not really familiar with the whole flashing thing (last
time I flashed my &lt;span class="caps"&gt;HTC&lt;/span&gt; Desire &lt;span class="caps"&gt;HD&lt;/span&gt; with some CyanogenMod fork back in summer
2013), the entire process went smoothly. At least kind of.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/android-5-lollipop1.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/android-5-lollipop1-483x483.jpg" alt="Android 5 "Lollipop"" title="Android 5 "Lollipop""&gt;&lt;img class="medium lazyload" data-src="/images/thumb/android-5-lollipop1-364x364.jpg" alt="Android 5 "Lollipop"" title="Android 5 "Lollipop""&gt;&lt;img class="small lazyload" data-src="/images/thumb/android-5-lollipop1-480x480.jpg" alt="Android 5 "Lollipop"" title="Android 5 "Lollipop""&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/android-5-lollipop2.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/android-5-lollipop2-483x483.jpg" alt="Android 5 "Lollipop"" title="Android 5 "Lollipop""&gt;&lt;img class="medium lazyload" data-src="/images/thumb/android-5-lollipop2-364x364.jpg" alt="Android 5 "Lollipop"" title="Android 5 "Lollipop""&gt;&lt;img class="small lazyload" data-src="/images/thumb/android-5-lollipop2-480x480.jpg" alt="Android 5 "Lollipop"" title="Android 5 "Lollipop""&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;This post is more about the things I messed up because they changed or were
different to flashing on the &lt;span class="caps"&gt;HTC&lt;/span&gt; Desire &lt;span class="caps"&gt;HD&lt;/span&gt; and thus is a note to myself what I
should remember in the future.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The ArchLinux &lt;tt class="docutils literal"&gt;[community]&lt;/tt&gt; repository has a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;android-tools&lt;/span&gt;&lt;/tt&gt; package that
provides &lt;tt class="docutils literal"&gt;adb&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;fastboot&lt;/tt&gt;. No problems at that point.&lt;/li&gt;
&lt;li&gt;Everything that is stored under &lt;tt class="docutils literal"&gt;/sdcard/&lt;/tt&gt; isn&amp;#8217;t on an actual &lt;span class="caps"&gt;SD&lt;/span&gt; card, but
internal storage! Guess what, I didn&amp;#8217;t realize that until I ran &lt;tt class="docutils literal"&gt;fastboot
oem unlock&lt;/tt&gt; and eventually booted the device later. The device warned me
that all user data will be wiped - fair enough - but who thought wiping data
in &lt;tt class="docutils literal"&gt;/sdcard/&lt;/tt&gt; is a good idea? Anyway, the data couldn&amp;#8217;t have been
important, I had no backup :D&lt;/li&gt;
&lt;li&gt;Be patient during the first boot after flashing the image! The process takes
time. It took about 20 minutes (guessing, didn&amp;#8217;t measure it).&lt;/li&gt;
&lt;li&gt;Remember your two factor authentication setup. Make sure to have the backup
keys for your Google account or you might not be able to login, because you
just wiped the second part of the &lt;span class="caps"&gt;2FA&lt;/span&gt; setup. If you&amp;#8217;re still logged in to
your Google account on a second device, you can temporarily turn off &lt;span class="caps"&gt;2FA&lt;/span&gt;,
setup your smartphone again and activate &lt;span class="caps"&gt;2FA&lt;/span&gt; again. Make sure you make a copy
of the &lt;strong&gt;new&lt;/strong&gt; backup codes. The old ones won&amp;#8217;t work anymore. Same goes for
e.g. GitHub: you can use an active login to re-enable two factor
authentication&lt;/li&gt;
&lt;li&gt;If you backup some Chrome data (bookmarks, open tabs, extensions, autofill,
&amp;#8230;), make sure you have the password to get your data back on the
smartphone. Open &lt;tt class="docutils literal"&gt;about:chrome&lt;/tt&gt; in Chrome and check the value for &amp;#8220;Explicit
Passphrase&amp;#8221;. If it&amp;#8217;s &lt;tt class="docutils literal"&gt;true&lt;/tt&gt;, go and find your password. If it&amp;#8217;s &lt;tt class="docutils literal"&gt;false&lt;/tt&gt;,
the data is encrypted with your  Google account password, you only need that
one.&lt;/li&gt;
&lt;li&gt;If you use Google Play Music or Spotify or similar apps with loads of offline
data, make sure you have enough time to sync them or you might find yourself
on a train without Internet connectivity and without Music :(&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Misc"></category><category term="Android"></category><category term="Lessons Learned"></category></entry><entry><title>Festival of Lights 2014</title><link href="https://markusholtermann.eu/2014/10/festival-of-lights-2014/" rel="alternate"></link><published>2014-10-20T00:00:00+02:00</published><updated>2014-10-20T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-10-20:/2014/10/festival-of-lights-2014/</id><summary type="html">&lt;p class="first last"&gt;From October 10 to 19, 2014 the 10. Festival of Lights happened in
Berlin after its foundation in 2005. This year&amp;#8217;s theme was &amp;#8220;Colours of Joy&amp;#8221;.
During those 10 nights many monuments and important places and buildings are
turned into artworks by national and international artists.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;From October 10 to 19, 2014 the &lt;a class="reference external" href="http://festival-of-lights.de/en/"&gt;10. Festival of Lights&lt;/a&gt; happened in Berlin
after its foundation in 2005. This year&amp;#8217;s theme was &amp;#8220;Colours of Joy&amp;#8221;. During
those 10 nights many monuments and important places and buildings are turned
into artworks by national and international artists.&lt;/p&gt;
&lt;p&gt;Unfortunately I didn&amp;#8217;t manage to visit the illuminated attractions earlier that
week, so I only had few hours on Sunday evening. Sadly I didn&amp;#8217;t have enough
time to all places, but only a few. But the whole event was a blast and the
productions really impressive. Too bad I didn&amp;#8217;t hear about it before this year.&lt;/p&gt;
&lt;p&gt;I took the photos with my Nexus 5, so don&amp;#8217;t expect them to be &lt;span class="caps"&gt;DSLR&lt;/span&gt; quality. I
additionally played around with some of Google&amp;#8217;s graphic effects filters, in
particular the black-white filter.&lt;/p&gt;
&lt;h2&gt;Brandenburg Gate&lt;/h2&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol01-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol01-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol01-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol01-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol01.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol01-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol01-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol01-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol02-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol02-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol02-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol02-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol02.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol02-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol02-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol02-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol03-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol03-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol03-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol03-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol03.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol03-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol03-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol03-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol04-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol04-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol04-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol04-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol04.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol04-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol04-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol04-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol05-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol05-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol05-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol05-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol05.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol05-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol05-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol05-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;Potsdam Square&lt;/h2&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol06-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol06-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol06-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol06-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol06.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol06-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol06-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol06-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol07-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol07-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol07-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol07-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol07.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol07-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol07-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol07-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;Humboldt University and Hotel de Rome&lt;/h2&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol08-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol08-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol08-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol08-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol08.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol08-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol08-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol08-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol09-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol09-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol09-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol09-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol09.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol09-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol09-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol09-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol10-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol10-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol10-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol10-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol10.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol10-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol10-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol10-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol11-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol11-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol11-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol11-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol11.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol11-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol11-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol11-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol12-bw.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol12-bw-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol12-bw-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol12-bw-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/festival-of-lights2014/fol12.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/festival-of-lights2014/fol12-483x483.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/festival-of-lights2014/fol12-364x364.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/festival-of-lights2014/fol12-480x480.jpg" alt="10. Festival of Lights, Berlin 2014" title="10. Festival of Lights, Berlin 2014"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;License&lt;/h2&gt;
&lt;div class="figure align-right"&gt;
&lt;a class="reference external image-reference" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;&lt;img alt="Creative Commons License" src="/images/cc-by-nc-sa-4.0-88x31.png" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;The photos in this blog post are licensed under a &lt;a class="reference external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 International License.&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Misc"></category><category term="Berlin"></category></entry><entry><title>Berliner Schulen in Zahlen</title><link href="https://markusholtermann.eu/de/2014/09/berliner-schulen-in-zahlen/" rel="alternate"></link><published>2014-09-30T00:00:00+02:00</published><updated>2014-09-30T00:00:00+02:00</updated><author><name>Alexander Grießer</name></author><id>tag:markusholtermann.eu,2014-09-30:/de/2014/09/berliner-schulen-in-zahlen/</id><summary type="html">&lt;p class="first last"&gt;Eine Übersicht über die Schulen in Berlin.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Berlin. 2014. 1093 Bildungseinrichtungen. 835 Zeilen Javascript Code. 573
Zeilen &lt;span class="caps"&gt;HTML&lt;/span&gt; Code. 347 Zeilen Python Code. 31 Schularten. 29 Schulzweige. 19
Ausstattungskriterien. 15 Sprachen. 12 Stadtteile. 2 Möglichkeiten das selbe zu
sagen. 1 Seminar: Ini 2.0.&lt;/p&gt;
&lt;p&gt;Wie soll man nun bei den ganzen Zahlen den Überblick bewahren? Das war die
Fragestellung mit wir uns im Seminar &amp;#8220;Data Science &amp;#8212; Hacking Society&amp;#8221; der Ini
2.0 im Sommersemester 2014 an der &lt;span class="caps"&gt;TU&lt;/span&gt; Berlin auseinandergesetzt haben. Aber
zunächst zurück zum Anfang des Projektes.&lt;/p&gt;
&lt;p&gt;Im Wintersemester 13/14 fragten wir bei der Stadt Berlin Daten zu den
Bildungseinrichtungen im Stadtgebiet an. Dass die Stadt Berlin sehr
detaillierte Daten zu allen Bildungseinrichtungen erfasst, konnten wir anhand
der schon existierenden &lt;a class="reference external" href="http://www.berlin.de/sen/bildung/schulverzeichnis_und_portraets/anwendung/"&gt;Recherche Webseite&lt;/a&gt; erahnen. Die Usability dieser
Webseite ist allerdings nicht die allerbeste, außerdem wollten wir auch die zu
Grunde liegenden Daten auswerten.&lt;/p&gt;
&lt;p&gt;Bis die Anfrage nach den Daten befriedigend beantwortet wurde verging leider
recht viel Zeit, daher war im Wintersemester 2013/2014 nur noch Zeit zum ersten
Sichten und Aufräumen der Datensätze vorhanden. Uns wurde eine Reihe von
Excel-Dokumenten zur Verfügung gestellt, welche glücklicherweise sehr
ordentlich waren. Einen ersten Prototyp einer besseren Rechercheanwendung
konnten wir zwar beginnen, leider aber nicht fertig stellen.&lt;/p&gt;
&lt;p&gt;Das gab den nachfolgenden Studenten im Sommersemester 2014 aber die Möglichkeit
mit einem schon vorhandenen Datensatz sehr schnell arbeiten zu können. Das Ziel
waren weitere Auswertungen der Daten, erneute Überprüfung der Datenqualität und
natürlich das Erstellen einer Webseite, welche die Recherche im Datensatz sehr
angenehm macht.&lt;/p&gt;
&lt;h2&gt;Datenqualität&lt;/h2&gt;
&lt;p&gt;Trotz recht aktuellen Daten waren diese an vielen Stellen nicht 100% korrekt.
Das mitunter größte Fehlerpotential boten Links zu den Webseiten der
Bildungseinrichtungen. Etwa 10% aller Links waren fehlerhaft und verwiesen auf
nicht mehr existierende Seiten.&lt;/p&gt;
&lt;p&gt;Obwohl die Daten zentral gesammelt werden, scheint die Erfassung nicht auf
elektronischem Wege, sondern nach wie vor mit Papierfragebogen und Stift
abzulaufen. Anders lassen sich inkonsistente Daten wie &amp;#8220;Computerraum&amp;#8221; und
&amp;#8220;Computerräume&amp;#8221; oder &amp;#8220;Werkstatt&amp;#8221; und &amp;#8220;Werkstätten&amp;#8221; nicht wirklich erklären.&lt;/p&gt;
&lt;h2&gt;Webanwendung&lt;/h2&gt;
&lt;p&gt;Bilder sagen mehr als tausend Worte, daher hier ein paar Eindrücke der
Webseite:&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/berlin-school-data/school1.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school1-315x315.jpg" alt="Startseite" title="Startseite"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school1-364x364.jpg" alt="Startseite" title="Startseite"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school1-480x480.jpg" alt="Startseite" title="Startseite"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/berlin-school-data/school1.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school1-315x315.jpg" alt="Ein paar angewendete Filter" title="Ein paar angewendete Filter"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school1-364x364.jpg" alt="Ein paar angewendete Filter" title="Ein paar angewendete Filter"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school1-480x480.jpg" alt="Ein paar angewendete Filter" title="Ein paar angewendete Filter"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/berlin-school-data/school3.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school3-315x315.jpg" alt="Detailansicht einer Schule" title="Detailansicht einer Schule"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school3-364x364.jpg" alt="Detailansicht einer Schule" title="Detailansicht einer Schule"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school3-480x480.jpg" alt="Detailansicht einer Schule" title="Detailansicht einer Schule"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/berlin-school-data/school4.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school4-315x315.jpg" alt="Heatmap Betreuungsschlüssel" title="Heatmap Betreuungsschlüssel"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school4-364x364.jpg" alt="Heatmap Betreuungsschlüssel" title="Heatmap Betreuungsschlüssel"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school4-480x480.jpg" alt="Heatmap Betreuungsschlüssel" title="Heatmap Betreuungsschlüssel"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/berlin-school-data/school5.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school5-315x315.jpg" alt="Impressum" title="Impressum"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school5-364x364.jpg" alt="Impressum" title="Impressum"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school5-480x480.jpg" alt="Impressum" title="Impressum"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Bis jetzt sind wir ohne technische Details ausgekommen, aber so ganz kommen wir
in diesem Abschnitt nicht drum herum. Die Webseite ist als sogenannte
&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Single-page_application"&gt;Single-Page-App&lt;/a&gt; umgesetzt, so dass bei Benutzung des Filters oder bei der
Navigation immer nur Teile der Seite neu geladen werden. Designtechnisch haben
wir uns auf &lt;a class="reference external" href="http://foundation.zurb.com/"&gt;Zurb&amp;#8217;s Foundation&lt;/a&gt; verlassen und das Standardtheme praktisch
nicht angepasst, daher sieht das auch so gut aus ;)&lt;/p&gt;
&lt;p&gt;Für die dynamische Programmierung haben wir &lt;a class="reference external" href="https://angularjs.org/"&gt;AngularJS&lt;/a&gt; verwendet, eine
Javascript Bibliothek welche wir anhand dieses Projekts kennen gelernt haben.
Die Daten der App lagern am Ende in einer &lt;a class="reference external" href="http://www.elasticsearch.org/"&gt;Elasticsearch Instanz&lt;/a&gt; und können
über &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Ajax_%28Programmierung%29"&gt;&lt;span class="caps"&gt;AJAX&lt;/span&gt;&lt;/a&gt; Zugriffe abgefragt werden.&lt;/p&gt;
&lt;p&gt;Wer sich für den Code interessiert, diesen findet man in einem &lt;a class="reference external" href="https://github.com/MarkusH/berlin-school-data"&gt;öffentlichen
Github Projekt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ein Artikel zu den technischen Details wurde von Alex und Markus
&lt;a class="reference external" href="https://markusholtermann.eu/2014/08/using-elasicsearch-as-relational-data-storage/"&gt;veröffentlicht&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Ausblick&lt;/h2&gt;
&lt;p&gt;Wir haben viel darüber gelernt wie aufwendig es ist solche Daten bei der
öffentlichen Verwaltung anzufragen. Ein Mitglied aus unserem Team ist auch
motiviert weiter zu machen, allerdings ist in Baden-Württemberg, trotz
Bestandteils des Koalitionsvertrags, noch kein Informationsfreiheitsgesetz in
Aussicht. Erwartungsgemäß ist die erste Anfrage nach einem ähnlich
umfangreichen Datensatz auch nicht zufriedenstellend beantwortet worden: einen
zentralen Datensatz gibt es nicht. Noch dazu soll für eine Auflistung aller
Adressen über 100 &lt;span class="caps"&gt;EUR&lt;/span&gt; gezahlt werden. Der nächste Schritt ist dort also mal auf
entsprechende Landtagsabgeordnete zuzugehen und sie an gewisse Inhalte ihres
Koalitionsvertrags zu erinnern.&lt;/p&gt;
</content><category term="Misc"></category><category term="Berlin"></category><category term="Projekte"></category><category term="Studium"></category></entry><entry><title>Django 1.7: Database Migrations done right</title><link href="https://markusholtermann.eu/2014/09/django-17-database-migrations-done-right/" rel="alternate"></link><published>2014-09-17T00:00:00+02:00</published><updated>2014-09-17T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-09-17:/2014/09/django-17-database-migrations-done-right/</id><summary type="html">&lt;p class="first last"&gt;About two weeks ago Django 1.7 was released with a lot of new and
shiny features, such the App Loading Refactoring, Custom Lookups, the Checks
Framework the buildin Migration support and many more. The release notes are
the longest in Django&amp;#8217;s history and span approx. 1,800 lines.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;About two weeks ago &lt;a class="reference external" href="https://www.djangoproject.com/weblog/2014/sep/02/release-17-final/"&gt;Django 1.7 was released&lt;/a&gt; with a lot of new and shiny
features, such the &lt;em&gt;App Loading Refactoring&lt;/em&gt;, &lt;em&gt;Custom Lookups&lt;/em&gt;, the &lt;em&gt;Checks
Framework&lt;/em&gt; the &lt;em&gt;buildin Migration support&lt;/em&gt; and many more. The release notes are
the longest in Django&amp;#8217;s history and span approx. 1,800 lines.&lt;/p&gt;
&lt;p&gt;This article is inspired by the questions and problems I saw on Django&amp;#8217;s &lt;span class="caps"&gt;IRC&lt;/span&gt;
support channel &lt;tt class="docutils literal"&gt;#django&lt;/tt&gt; on &lt;a class="reference external" href="http://freenode.net/"&gt;Freenode&lt;/a&gt; regarding the new migration
framework &lt;tt class="docutils literal"&gt;django.db.migrations&lt;/tt&gt;. I&amp;#8217;ll show different problems developers ran
into, explain why their particular way of doing something doesn&amp;#8217;t work
(anymore) and how to do this now.&lt;/p&gt;
&lt;h2&gt;Why not integrate South into Django?&lt;/h2&gt;
&lt;p&gt;A handful of people asked why &lt;a class="reference external" href="http://south.aeracode.org/"&gt;South&lt;/a&gt; hasn&amp;#8217;t just been integrated into Django,
so all the old migrations would &lt;em&gt;just work&lt;/em&gt;. There are many reasons why &lt;a class="reference external" href="http://www.aeracode.org/"&gt;Andrew
Godwin&lt;/a&gt;, the author of South, a Django core developer and the author of
&lt;tt class="docutils literal"&gt;django.db.migrations&lt;/tt&gt; did it differently. But one of the main reasons is
probably the way how South stores the state the database has over time, the so
called &lt;em&gt;frozen &lt;span class="caps"&gt;ORM&lt;/span&gt;&lt;/em&gt;. When you look at a regular sized migration file you often
have about 50 lines of forwards and backwards code and a often continuously
growing &lt;em&gt;frozen &lt;span class="caps"&gt;ORM&lt;/span&gt;&lt;/em&gt; below.&lt;/p&gt;
&lt;p&gt;This is clearly not the best solution. Which is why another way of detecting
the changes between the current implementation of your app / project and the
database state created by the previous migrations had to be found.&lt;/p&gt;
&lt;p&gt;Django 1.7 behaves differently. Whenever you run &lt;tt class="docutils literal"&gt;python manage.py
makemigrations myapp&lt;/tt&gt;, Django first applies all migration changes internally
by constructing the models and fields with their properties on-the-fly. In the
second step Django compares the state generated from all migration files to the
state presented in your code. All differences will be considered changes that
will be written in new migration files.&lt;/p&gt;
&lt;div class="admonition hint"&gt;
&lt;p class="first admonition-title"&gt;Hint&lt;/p&gt;
&lt;p class="last"&gt;One thing you might notice at some point, the models created by Django
on-the-fly belong to the Python module &lt;tt class="docutils literal"&gt;__fake__&lt;/tt&gt;. This is the reason why
you won&amp;#8217;t be able to access custom function on the model and why e.g. an
overwritten &lt;tt class="docutils literal"&gt;save()&lt;/tt&gt; will not be called.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To work around circular dependencies, Django splits a single migration file
into multiple within the same app. The resulting dependencies are declared in a
list at the top of a &lt;tt class="docutils literal"&gt;Migration&lt;/tt&gt; class:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;thisapp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;swappable_dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AUTH_USER_MODEL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;otherapp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0002_auto_20140915_1402&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;# some operations&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;How do dependencies between migrations work?&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s assume you have two Django apps &lt;tt class="docutils literal"&gt;author&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;book&lt;/tt&gt; with these
models:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# author/models.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;birthday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Birthday&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# book/models.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveSmallIntegerField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What happens when you call &lt;tt class="docutils literal"&gt;python manage.py makemigrations&lt;/tt&gt;? First of all,
since no apps are given, Django reads the migrations from all apps listed in
&lt;tt class="docutils literal"&gt;INSTALLED_APPS&lt;/tt&gt;. In our case, this is &lt;tt class="docutils literal"&gt;('author', &lt;span class="pre"&gt;'book',)&lt;/span&gt;&lt;/tt&gt;. In the next
step Django replays all migrations it can find for those apps internally, as
explained above. Since there are no migrations yet, Django is going to start
from scratch:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;:
  0001_initial.py:
    - Create model Book
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;:
  0001_initial.py:
    - Create model Author
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As a result, you will end up with two migrations:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# author/migrations/0001_initial.py&lt;/span&gt;
&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unicode_literals&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;birthday&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Birthday&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;bases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="c1"&gt;# book/migrations/0001_initial.py&lt;/span&gt;
&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unicode_literals&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto_created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveSmallIntegerField&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;bases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you now run &lt;tt class="docutils literal"&gt;python manage.py migrate&lt;/tt&gt; Django looks at all migrations
that haven&amp;#8217;t been applied yet and will perform all their operations in-order,
starting with the migrations that have no dependencies to migrations that
haven&amp;#8217;t been applied. In the case above, this is &lt;tt class="docutils literal"&gt;author.0001_initial&lt;/tt&gt;. The
&lt;tt class="docutils literal"&gt;dependencies&lt;/tt&gt; list is empty. Afterwards &lt;tt class="docutils literal"&gt;book.0001_initial&lt;/tt&gt; can be
applied, because all dependencies are resolved.&lt;/p&gt;
&lt;div class="admonition hint"&gt;
&lt;p class="first admonition-title"&gt;Hint&lt;/p&gt;
&lt;p class="last"&gt;Django will try to run the migrations ordered by the name of the app they
belong to. But obviously only if there are no dependencies that have to be
resolved first.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can see all migrations in all installed apps, as well as their status
(applied or not) by running &lt;tt class="docutils literal"&gt;python manage.py migrate &lt;span class="pre"&gt;--list&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py migrate --list
author
 &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; 0001_initial
book
 &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; 0001_initial
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What is the &amp;#8220;(&amp;#8216;myapp&amp;#8217;, &amp;#8216;__first__&amp;#8217;)&amp;#8221; dependency?&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s say, &lt;tt class="docutils literal"&gt;author&lt;/tt&gt; is a third party app that doesn&amp;#8217;t ship with Django
migrations. Remove the migrations folder &lt;tt class="docutils literal"&gt;author/migrations&lt;/tt&gt; as well as
&lt;tt class="docutils literal"&gt;book/migrations/0001_initial.py&lt;/tt&gt; to start over. When you now run
&lt;tt class="docutils literal"&gt;makemigrations`&lt;/tt&gt; you will end up with a single migration file for &lt;tt class="docutils literal"&gt;book&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python manage.py makemigrations
Migrations &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;:
  0001_initial.py:
    - Create model Book
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;#book/migrations/0001_initial.py&lt;/span&gt;
&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unicode_literals&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__first__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AutoField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto_created&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositiveSmallIntegerField&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;author.Author&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;bases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The dependency &lt;tt class="docutils literal"&gt;('author', &lt;span class="pre"&gt;'__first__'),&lt;/span&gt;&lt;/tt&gt; tells Django to apply a migration
at some point after the first migration in the referenced app, independent of
its name.&lt;/p&gt;
&lt;h2&gt;How do I add a data migration?&lt;/h2&gt;
&lt;p&gt;If you used &lt;a class="reference external" href="http://south.aeracode.org/"&gt;South&lt;/a&gt; you might know about the &lt;tt class="docutils literal"&gt;datamigration&lt;/tt&gt; command that
simply creates a new migration file in the given app and optionally adds some
models to the frozen &lt;span class="caps"&gt;ORM&lt;/span&gt; state.&lt;/p&gt;
&lt;p&gt;In Django 1.7 it is way simpler: run
&lt;tt class="docutils literal"&gt;python manage.py makemigrations &lt;span class="pre"&gt;--empty&lt;/span&gt; myapp&lt;/tt&gt; You can rename the generated
file if you like as long as it ends with &lt;tt class="docutils literal"&gt;.py&lt;/tt&gt; and doesn&amp;#8217;t contain a &lt;tt class="docutils literal"&gt;.&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unicode_literals&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;myapp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Within this migration you can now add the &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.7/ref/migration-operations/#special-operations"&gt;operations&lt;/a&gt; you want to perform.
For data migrations you can use &lt;tt class="docutils literal"&gt;migrations.RunSQL&lt;/tt&gt; or
&lt;tt class="docutils literal"&gt;migrations.RunPython&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="admonition important"&gt;
&lt;p class="first admonition-title"&gt;Important&lt;/p&gt;
&lt;p class="last"&gt;If you add an empty migration file to an app and want to run operations
that require another app to be migrated to a specific state, you &lt;em&gt;have to&lt;/em&gt;
add the required dependencies explicitly!&lt;/p&gt;
&lt;/div&gt;
&lt;h3&gt;Running native &lt;span class="caps"&gt;SQL&lt;/span&gt; commands during migrations&lt;/h3&gt;
&lt;p&gt;If you are able to express your data changes in &lt;span class="caps"&gt;SQL&lt;/span&gt;, &lt;em&gt;please&lt;/em&gt; do so, this will
be faster than through the &lt;span class="caps"&gt;ORM&lt;/span&gt;. But keep in mind, that this might not be the
solution if you have to fight multiple database back-ends.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;RunSQL&lt;/tt&gt; accepts 1 to 3 arguments: &lt;tt class="docutils literal"&gt;sql&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;reverse_sql&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;state_operations&lt;/tt&gt;. &lt;tt class="docutils literal"&gt;sql&lt;/tt&gt; is required and expects a string (that may
consist of multiple statements).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;UPDATE myapp_mymodel SET col1 = col2 + col3;&amp;quot;&lt;/span&gt;
                  &lt;span class="s2"&gt;&amp;quot;UPDATE myapp_mymodel SET col2 = col3 * col3;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you don&amp;#8217;t specify the &lt;tt class="docutils literal"&gt;reverse_sql&lt;/tt&gt; argument, you won&amp;#8217;t be able to
roll-back beyond this migration. The default is &lt;tt class="docutils literal"&gt;None&lt;/tt&gt;, using &lt;tt class="docutils literal"&gt;&amp;quot;&lt;span class="caps"&gt;SELECT&lt;/span&gt; 1;&amp;quot;&lt;/tt&gt;
is fine for a roll-back.&lt;/p&gt;
&lt;p&gt;With the &lt;tt class="docutils literal"&gt;state_operations&lt;/tt&gt; attribute you are able to modify the model state
Django internally constructs while running the migrations. I haven&amp;#8217;t seen a
usecase for that yet.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;As of time of writing, if you want to use &lt;tt class="docutils literal"&gt;%&lt;/tt&gt; as a wildecard in e.g. the
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/tt&gt;- clause, you need to escape it with another &lt;tt class="docutils literal"&gt;%&lt;/tt&gt; character
(&lt;a class="reference external" href="https://code.djangoproject.com/ticket/23426"&gt;Django issue #23426&lt;/a&gt;):&lt;/p&gt;
&lt;div class="last"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;UPDATE myapp_mymodel SET col1 = &amp;#39;a&amp;#39; WHERE col2 LIKE &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%%&lt;/span&gt;&lt;span class="s2"&gt;val&lt;/span&gt;&lt;span class="si"&gt;%%&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;;&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Run custom Python code during migrations&lt;/h3&gt;
&lt;p&gt;Apart from the &lt;tt class="docutils literal"&gt;RunSQL&lt;/tt&gt; operation Django 1.7 comes with a &lt;tt class="docutils literal"&gt;RunPython&lt;/tt&gt;
operation. This allows you to run custom Python function during a forwards or a
backwards migration.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;RunPython&lt;/tt&gt; accepts 1 to 3 arguments: &lt;tt class="docutils literal"&gt;code&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;reverse_code&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;atomic&lt;/tt&gt;. &lt;tt class="docutils literal"&gt;code&lt;/tt&gt; is require and accepts any callable with two arguments, so
does &lt;tt class="docutils literal"&gt;reverse_code&lt;/tt&gt; which is optional, though. &lt;tt class="docutils literal"&gt;atomic&lt;/tt&gt; defaults to
&lt;tt class="docutils literal"&gt;True&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Please keep in mind that a &lt;tt class="docutils literal"&gt;reverse_code&lt;/tt&gt; of &lt;tt class="docutils literal"&gt;None&lt;/tt&gt; (the default) prevents
the migration from being rolled back. If you want to be able to roll-back,
because your Python code in &lt;tt class="docutils literal"&gt;code&lt;/tt&gt; computes some initial data for every row
in a newly added column, add something like &lt;tt class="docutils literal"&gt;lambda x, y: None&lt;/tt&gt; as
&lt;tt class="docutils literal"&gt;reverse_code&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;For more details on the &lt;tt class="docutils literal"&gt;RunPython&lt;/tt&gt; operation please see the &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.7/ref/migration-operations/#runpython"&gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The callable for e.g. upload_to cannot be found&lt;/h2&gt;
&lt;p&gt;There are a few model fields out their that take callables as arguments to do
further processing. One of those fields is the &lt;tt class="docutils literal"&gt;FileField&lt;/tt&gt; that has an
&lt;tt class="docutils literal"&gt;upload_to&lt;/tt&gt; argument which accepts a string as well as a function to
dynamically derive the storage location. To make migrations work automatically,
this function has to be directly importable from a package or module.&lt;/p&gt;
&lt;p&gt;The same goes for classes for custom fields: The way Python works doesn&amp;#8217;t allow
importing inner classes. Move the class to the module level and you&amp;#8217;ll be fine.&lt;/p&gt;
&lt;p&gt;See the chapter about &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.7/topics/migrations/#serializing-values"&gt;serializing values&lt;/a&gt; in the docs.&lt;/p&gt;
&lt;h2&gt;Backwards migrations roll-back too many operations&lt;/h2&gt;
&lt;p&gt;The way Django handles the order of migrations and the fact that Django
strictly enforces dependencies between migration to be present during
migration, is different compared to South. While the forwards migration plans
won&amp;#8217;t really differ from South&amp;#8217;s, Django behaves completely different when it
comes to backwards migrations (at least in 1.7, follow &lt;a class="reference external" href="https://code.djangoproject.com/ticket/23426"&gt;Django issue #23474&lt;/a&gt;
for updates).&lt;/p&gt;
&lt;p&gt;By design Django will roll back the database to the state it would have if you
roll forward and stop after a given migration. To make this more clear, let&amp;#8217;s
take the following scenario from the Django tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;app_a:  0001 &amp;lt;-- 0002 &amp;lt;--- 0003 &amp;lt;-- 0004
                         /
app_b:  0001 &amp;lt;-- 0002 &amp;lt;-/
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you run &lt;tt class="docutils literal"&gt;python manage.py migrate&lt;/tt&gt; you will end up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;[X] app_a.0001
[X] app_a.0002 ... (depends on app_a.0001)
[X] app_b.0001
[X] app_b.0002 ... (depends on app_b.0001)
[X] app_a.0003 ... (depends on app_a.0002, app_b.0002)
[X] app_a.0004 ... (depends on app_a.0003)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you run &lt;tt class="docutils literal"&gt;python manage.py migrate app_a 0003&lt;/tt&gt; from this state, you will
end up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;[X] app_a.0001
[X] app_a.0002 ... (depends on app_a.0001)
[X] app_b.0001
[X] app_b.0002 ... (depends on app_b.0001)
[X] app_a.0003 ... (depends on app_a.0002, app_b.0002)
[ ] app_a.0004 ... (depends on app_a.0003)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;being applied.&lt;/p&gt;
&lt;p&gt;The difference happens when you roll-back past a dependency.&lt;/p&gt;
&lt;p&gt;If you run &lt;tt class="docutils literal"&gt;python manage.py migrate app_a 0002&lt;/tt&gt; from the initial state, you
will end up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;[X] app_a.0001
[X] app_a.0002 ... (depends on app_a.0001)
[X] app_b.0001
[X] app_b.0002 ... (depends on app_b.0001)
[ ] app_a.0003 ... (depends on app_a.0002, app_b.0002)
[ ] app_a.0004 ... (depends on app_a.0003)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;being applied.&lt;/p&gt;
&lt;p&gt;But if you run &lt;tt class="docutils literal"&gt;python manage.py migrate app_b 0002&lt;/tt&gt;, from the initial state,
you will end up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;[X] app_a.0001
[X] app_a.0002 ... (depends on app_a.0001)
[X] app_b.0001
[X] app_b.0002 ... (depends on app_b.0001)
[ ] app_a.0003 ... (depends on app_a.0002, app_b.0002)
[ ] app_a.0004 ... (depends on app_a.0003)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;being applied.&lt;/p&gt;
&lt;p&gt;Do you recognize the missing &lt;tt class="docutils literal"&gt;app_a.0003&lt;/tt&gt; here.&lt;/p&gt;
</content><category term="Development"></category><category term="Database"></category><category term="Django"></category><category term="Migration"></category></entry><entry><title>Django file and memory mail back-end for tests</title><link href="https://markusholtermann.eu/2014/09/django-file-and-memory-mail-backend/" rel="alternate"></link><published>2014-09-15T00:00:00+02:00</published><updated>2014-09-15T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-09-15:/2014/09/django-file-and-memory-mail-backend/</id><summary type="html">&lt;p class="first last"&gt;Whenever you write unit or integration tests for your Django
application that involves sending mails, you end up with the LocMem mail
back-end as a default set by Django during test setup. However, if you want
to have a look at all the mails that have been sent, you are probably going
to choose the File mail back-end.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Whenever you write unit or integration tests for your Django application that
involves sending mails, you end up with the &lt;tt class="docutils literal"&gt;LocMem&lt;/tt&gt; mail back-end
(&lt;tt class="docutils literal"&gt;django.core.mail.backends.console .EmailBackend&lt;/tt&gt;) as a default set by
Django during test setup. However, if you want to have a look at all the mails
that have been sent, you are probably going to choose the &lt;tt class="docutils literal"&gt;File&lt;/tt&gt; mail
back-end (&lt;tt class="docutils literal"&gt;django.core.mail.backends.filebased.EmailBackend&lt;/tt&gt;) [&lt;a class="reference external" href="https://docs.djangoproject.com/en/1.7/topics/email/#email-backends"&gt;docs&lt;/a&gt;].&lt;/p&gt;
&lt;p&gt;The former give you the possibility to check for the amount of mails that have
been sent and inspect them further through &lt;tt class="docutils literal"&gt;django.core.mail.outbox&lt;/tt&gt;, the
latter stores all mails in a text file for later inspection.&lt;/p&gt;
&lt;p&gt;So, if you are using the &lt;tt class="docutils literal"&gt;LocMem&lt;/tt&gt; back-end and want to inspect all mails
after an entire test run, you are first trying to change the back-end with
&lt;tt class="docutils literal"&gt;&amp;#64;override_settings&lt;/tt&gt; [&lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/topics/testing/tools/#django.test.override_settings"&gt;docs&lt;/a&gt;]. This works as long as you don&amp;#8217;t use
&lt;tt class="docutils literal"&gt;django.core.mail.outbox&lt;/tt&gt;, because this will always be &lt;tt class="docutils literal"&gt;None&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;[]&lt;/tt&gt;.
How are you going to make that work?&lt;/p&gt;
&lt;p&gt;When this question came up on the Django &lt;span class="caps"&gt;IRC&lt;/span&gt; channel I&amp;#8217;ll came up with&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mail&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.mail.backends.filebased&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EmailBackend&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;FileEmailBackend&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileEmailBackend&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EmailBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;outbox&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EmailBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EmailBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I didn&amp;#8217;t test the code myself, but the user asking for support seemed to find
it a working solution.&lt;/p&gt;
</content><category term="Development"></category><category term="Django"></category><category term="Testing"></category></entry><entry><title>Logitech mouse M705 double click problem</title><link href="https://markusholtermann.eu/2014/09/logitech-mouse-m705-double-click-problem/" rel="alternate"></link><published>2014-09-09T00:00:00+02:00</published><updated>2014-09-09T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-09-09:/2014/09/logitech-mouse-m705-double-click-problem/</id><summary type="html">&lt;p class="first last"&gt;A few years back, it must be around 2011, I bought a Logitech M705
mouse. A few days ago this mouse behaved wired. Almost every single left
click turned into a double click. Looking through the Internet it seemed to
be a common problem.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A few years back, it must be around 2011, I bought a &lt;a class="reference external" href="http://www.logitech.com/en-us/product/marathon-mouse-m705"&gt;Logitech M705 mouse&lt;/a&gt;. A
few days ago this mouse behaved wired. Almost every single left click turned
into a double click. Looking through the Internet it seemed to be a common
problem.&lt;/p&gt;
&lt;p&gt;I replaced the mouse with an even older one laying around, thinking I&amp;#8217;d
probably need to replace it with a new one sooner than later. Today though I
searched for a way to fix the double click problem. I ended up finding a
&lt;a class="reference external" href="http://www.youtube.com/watch?v=V66Cd2Y4IPs"&gt;YouTube video&lt;/a&gt; showing how to dismantle the mouse, fix a part and put the
components back together.&lt;/p&gt;
&lt;p&gt;Problem solved. The mouse is as good as new. At least for now.&lt;/p&gt;
</content><category term="Misc"></category><category term="Stuff"></category></entry><entry><title>Using Elasticsearch as Relational Data Storage</title><link href="https://markusholtermann.eu/2014/08/using-elasicsearch-as-relational-data-storage/" rel="alternate"></link><published>2014-08-10T00:00:00+02:00</published><updated>2014-08-10T00:00:00+02:00</updated><author><name>Alexander Grießer</name></author><id>tag:markusholtermann.eu,2014-08-10:/2014/08/using-elasicsearch-as-relational-data-storage/</id><summary type="html">&lt;p class="first last"&gt;This blog post is about a university project Alex and I have been
developing over the last months as part of our Master studies using
AngularJS and Elasticsearch.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This blog post is about a &lt;a class="reference external" href="https://github.com/MarkusH/berlin-school-data/"&gt;university project&lt;/a&gt; Alex, a fellow student at my
university, and I have been developing over the last months as part of our
Master studies. We are both quite experienced software developers focusing on
application and web development. As part of a university project we had
absolutely no requirements on a coding language or styling. As the only
requirement was being a web site and we both knew absolutely nothing about on
how to create single page applications, we looked around and eventually chose
&lt;a class="reference external" href="https://angularjs.org/"&gt;AngularJS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We didn&amp;#8217;t start from scratch, though. There was a prototype from last semester
from another team we used for inspiration. The only &lt;em&gt;problem&lt;/em&gt; with the project
was being written as a JavaEE application. The setup, even to make it run
locally, would be too complicated for the use case. Furthermore we both didn&amp;#8217;t
like the idea coding in Java; Alex already using Java at work and wanting to
expand his knowledge, and Markus trying to avoid Java for various reasons.&lt;/p&gt;
&lt;h2&gt;About the Project&lt;/h2&gt;
&lt;p&gt;The web site should list information about all kinds of schools in Berlin. A
user should be able to filter and search for schools by different criteria. The
intention of the university course and the project is to learn about &lt;em&gt;OpenData&lt;/em&gt;
and &lt;em&gt;Data Journalism&lt;/em&gt;. The city of Berlin offers a &lt;a class="reference external" href="http://www.berlin.de/sen/bildung/schulverzeichnis_und_portraets/anwendung/"&gt;similar service&lt;/a&gt; already,
although the user experience is just &lt;em&gt;&amp;#8220;meh&amp;#8221;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We took the data set the group acquired in the last semester and normalized it
even more. We also performed some additional cleaning. For example if a &lt;span class="caps"&gt;URL&lt;/span&gt; was
given for a school we checked whether that &lt;span class="caps"&gt;URL&lt;/span&gt; was reachable or not and
accordingly removed those URLs that couldn&amp;#8217;t be reached.&lt;/p&gt;
&lt;h2&gt;The Underlying Data Storage&lt;/h2&gt;
&lt;p&gt;Looking at the problem from a system architects point of view clearly shows
relations between many parts. While a school has a single address it might have
multiple types, like elementary school and high school. A school probably
teaches multiple languages, not only German but English, French, Dutch or
Chinese too. Thus a relational data store / database like PostgreSQL or MySQL
would be one&amp;#8217;s first choices.&lt;/p&gt;
&lt;p&gt;Contrary to what a system architect normally would do we chose a technology not
intended for the given use case: a vanilla &lt;a class="reference external" href="http://www.elasticsearch.org/"&gt;Elasticsearch&lt;/a&gt; instance. We chose
this technology for several reasons:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;We both professionally develop software using &lt;span class="caps"&gt;SQL&lt;/span&gt; databases. Therefore it was
unlikely that we would learn anything new while implementing such a easy
application.&lt;/li&gt;
&lt;li&gt;The data set contains a lot of optional data. So there was a requirement for
a &amp;#8220;dynamic&amp;#8221; data model for the schools.&lt;/li&gt;
&lt;li&gt;But most important: we wanted to learn something new that we could
potentially use in our further as developers&lt;/li&gt;
&lt;li&gt;And last but not least we wanted to figure out if one can only use
Elasticsearch, as this is way outside its common use cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Import of the Data&lt;/h3&gt;
&lt;p&gt;The original data source consists of several Excel documents. These were
provided by the city of Berlin and have been normalized by our fellow students
in the semester before. For easier handling we transformed those files to the
&lt;span class="caps"&gt;CSV&lt;/span&gt; format. Later these &lt;span class="caps"&gt;CSV&lt;/span&gt; files are used to import the data into the
Elasticsearch instance.&lt;/p&gt;
&lt;p&gt;Before doing this we created a data model bringing some of the features of a
relational database into Elasticsearch. Additionally this step is used to set
up and configure how Elasticsearch handles the data, mainly (not) analyzing and
(not) indexing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;school&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;bsn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;not_analyzed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;analyzed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;phonenumber&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;not_analyzed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;wwwaddress&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;no&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;address&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;properties&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;plz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;integer&amp;quot;&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;location&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;lat_lon&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;geo_point&amp;quot;&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;district&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;not_analyzed&amp;quot;&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;street&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;analyzed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;analyzed&amp;quot;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;nested&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the excerpt above we are using Elasticsearch&amp;#8217;s &lt;a class="reference external" href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-nested-type.html"&gt;Nested
Mapping Type&lt;/a&gt;. We decided to use this type over inner objects or &lt;a class="reference external" href="http://www.elasticsearch.org/guide/reference/mapping/parent-field.html"&gt;Parent/Child
Types&lt;/a&gt; for two reasons:&lt;/p&gt;
&lt;blockquote&gt;
[First,] each nested doc remains independent, and [one] can perform a query
like [&lt;tt class="docutils literal"&gt;address.district=Kreutzberg &lt;span class="caps"&gt;AND&lt;/span&gt; address.plz = 10999&lt;/tt&gt;] without a
problem. [Second], reading is faster than the parent/child because the
nested document is stored in the same Lucene block as the main document.
&lt;a class="citation-reference" href="#es13" id="id1"&gt;[&lt;span class="caps"&gt;ES13&lt;/span&gt;]&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Although writing may require re-indexing the entire document, this is no
problem for our use case as the data is imported once.&lt;/p&gt;
&lt;p&gt;The actual import was done by a small Python script using &lt;a class="reference external" href="http://click.pocoo.org/"&gt;Click&lt;/a&gt; that
connects to the Elasticsearch instance creates the necessary document structure
and later imports the different data types (base data, address data, school
profiles, etc.).&lt;/p&gt;
&lt;h2&gt;The Front-End&lt;/h2&gt;
&lt;p&gt;We are no designers. But we wanted to create a web-page that is both faster and
more functional than what the original web page of the city of Berlin offers.
We did not target mobile browsers in particular but if we could support them in
a reasonable way, then we would do that. Markus is a fan of &lt;a class="reference external" href="http://foundation.zurb.com/"&gt;Zurb Foundation&lt;/a&gt;,
therefore we decided to use this &lt;span class="caps"&gt;CSS&lt;/span&gt; framework as a starting point for our
layout.&lt;/p&gt;
&lt;p&gt;Since our data-store is an Elasticsearch instance we had the possibility to
retrieve data directly from the browser via &lt;span class="caps"&gt;JSON&lt;/span&gt; requests. Therefore we decided
to create a single page application and eventually chose AngularJS. We also
delved a little bit into the world of Website front-end development tooling by
using the current state of the art Bower, Grunt and Compass tooling.&lt;/p&gt;
&lt;p&gt;Our app has three important components:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The filter form&lt;/li&gt;
&lt;li&gt;A map view that shows the schools matching the current filter&lt;/li&gt;
&lt;li&gt;A detail page of every school&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/berlin-school-data/school1.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school1-483x483.jpg" alt="Startseite" title="Startseite"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school1-364x364.jpg" alt="Startseite" title="Startseite"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school1-480x480.jpg" alt="Startseite" title="Startseite"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/berlin-school-data/school2.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school2-483x483.jpg" alt="Ein paar angewendete Filter" title="Ein paar angewendete Filter"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school2-364x364.jpg" alt="Ein paar angewendete Filter" title="Ein paar angewendete Filter"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school2-480x480.jpg" alt="Ein paar angewendete Filter" title="Ein paar angewendete Filter"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/berlin-school-data/school3.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school3-483x483.jpg" alt="Detailansicht einer Schule" title="Detailansicht einer Schule"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school3-364x364.jpg" alt="Detailansicht einer Schule" title="Detailansicht einer Schule"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school3-480x480.jpg" alt="Detailansicht einer Schule" title="Detailansicht einer Schule"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/berlin-school-data/school4.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/berlin-school-data/school4-483x483.jpg" alt="Heatmap Betreuungsschlüssel" title="Heatmap Betreuungsschlüssel"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/berlin-school-data/school4-364x364.jpg" alt="Heatmap Betreuungsschlüssel" title="Heatmap Betreuungsschlüssel"&gt;&lt;img class="small lazyload" data-src="/images/thumb/berlin-school-data/school4-480x480.jpg" alt="Heatmap Betreuungsschlüssel" title="Heatmap Betreuungsschlüssel"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h3&gt;The Filter&lt;/h3&gt;
&lt;p&gt;The filter data is dynamically retrieved from Elasticsearch upon page load
using a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; &lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt; query to the search &lt;span class="caps"&gt;URL&lt;/span&gt; with a body like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;size&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;aggs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;nested&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;aggs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;districts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;terms&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;field&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;address.district&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;order&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;quot;_term&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;asc&amp;quot;&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;quot;size&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;nested&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;address&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;branches&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;quot;terms&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;field&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;branches&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;order&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;quot;_term&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;asc&amp;quot;&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;quot;size&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The idea is to aggregate all distinct values available for various fields. The
&lt;tt class="docutils literal"&gt;nested&lt;/tt&gt; block does that for the district (as this is a nested object), the
&lt;tt class="docutils literal"&gt;branches&lt;/tt&gt; block exemplary shows how it is done for direct attributes.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&amp;quot;size&amp;quot;: 0&lt;/tt&gt; definition in the outer block tells Elasticsearch to not
return any results entries. Inside the aggregation definition it makes
Elasticsearch return all distinct values.&lt;/p&gt;
&lt;h3&gt;The Map&lt;/h3&gt;
&lt;p&gt;The map component is responsible to display the result of a filter operation.
The map is shown using the &lt;a class="reference external" href="http://www.openlayers.org/"&gt;Openlayers&lt;/a&gt; JavaScript library.&lt;/p&gt;
&lt;h3&gt;The Detail Page&lt;/h3&gt;
&lt;p&gt;The detail page is quite straightforward. One requirement we defined for the
detail page was, that we should be able to provide a deep linking option. This
was actually quite easy to implement using the &lt;a class="reference external" href="https://docs.angularjs.org/api/ngRoute"&gt;ngRoute&lt;/a&gt; module of AngularJS.&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;URL&lt;/span&gt; is defined to look like this: &amp;#8220;domain.de/#/schools/&lt;span class="caps"&gt;BSN&lt;/span&gt;&amp;#8221;. The &lt;span class="caps"&gt;BSN&lt;/span&gt; is a
unique identifier for each school in Berlin (we assume it stands for Berlin
School Number). The &lt;tt class="docutils literal"&gt;ngRoute&lt;/tt&gt; module allows to specify parameter captures in
the route definition, so it&amp;#8217;s very easy to access parts of the current &lt;span class="caps"&gt;URL&lt;/span&gt; in
the JavaScript code. The route definition for the school detail page is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;$routeProvider&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$routeProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$routeProvider&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/schools/:schoolId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;templateUrl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;views/school.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SchoolCtrl&amp;#39;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using the schools identifier we make a simple lookup in Elasticsearch and get
the document for the school. Since Elasticsearch returns data in &lt;span class="caps"&gt;JSON&lt;/span&gt; format we
can just set the returned value in the scope of the detail page controller, the
layout will then be automatically updated by AngularJS.&lt;/p&gt;
&lt;h2&gt;Deployment&lt;/h2&gt;
&lt;p&gt;As already stated above, we chose a way for the implementation that lets us
circumvent the usage of an application server (as it would be needed for Java
or Python). Instead the page only requires a web server and Elasticsearch to
run.&lt;/p&gt;
&lt;h3&gt;Nginx Setup&lt;/h3&gt;
&lt;p&gt;An exemplary Nginx server config can look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt;        &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt;   &lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;gzip&lt;/span&gt;          &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gzip_types&lt;/span&gt;    &lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Config location&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/config.json&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;alias&lt;/span&gt;   &lt;span class="s"&gt;/var/www/config.json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Permit GET and POST to Elasticsearch on a certain index ...&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;/_es/school/([^/]+)/_search&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;limit_except&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;deny&lt;/span&gt;   &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;rewrite&lt;/span&gt;            &lt;span class="s"&gt;/_es/(.+)&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="s"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt;         &lt;span class="s"&gt;http://127.0.0.1:9200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt;   &lt;span class="s"&gt;Host&lt;/span&gt;            &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt;   &lt;span class="s"&gt;X-Real-IP&lt;/span&gt;       &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt;   &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# ... and deny everything else&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/_es&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Public part of the website&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt;    &lt;span class="s"&gt;/var/www/htdocs/public/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;index&lt;/span&gt;   &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By only allowing &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt; and restricting those queries to a
limited &lt;span class="caps"&gt;URL&lt;/span&gt; pattern we can make sure nobody can remove or add some data or even
drop the index.&lt;/p&gt;
&lt;h3&gt;Elasticsearch Setup&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;network.host: 127.0.0.1
path:
    conf: /etc/elasticsearch
    data: /var/lib/elasticsearch
    logs: /var/log/elasticsearch
    work: /tmp/elasticsearch
script.disable_dynamic: true
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apart from the protections of Elasticsearch mentioned above, it is &lt;strong&gt;highly
recommended&lt;/strong&gt; to disable dynamic scripting as this would potentially expose the
entire server to the outside world. Setting the Elasticsearch network host to
&lt;tt class="docutils literal"&gt;127.0.0.1&lt;/tt&gt; is also &lt;strong&gt;required&lt;/strong&gt;. Otherwise people could connect directly do
Elasticsearch and any of the Nginx protections wouldn&amp;#8217;t matter.&lt;/p&gt;
&lt;h3&gt;Website Config&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;elasticsearch&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;school&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;host&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;http://example.com/_es&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;heatmap&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;heatmap.json&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;quot;map&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;quot;feature_bubble&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/views/inc/map_feature_bubble.html&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Sources and Additional Reading&lt;/h2&gt;
&lt;table class="docutils citation" frame="void" id="es13" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[&lt;span class="caps"&gt;ES13&lt;/span&gt;]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Zachary Tong. Managing Relations inside Elasticsearch. February 20,
2013 &lt;a class="reference external" href="http://www.elasticsearch.org/blog/managing-relations-inside-elasticsearch/"&gt;http://www.elasticsearch.org/blog/managing-relations-inside-elasticsearch/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Development"></category><category term="AngularJS"></category><category term="Elasticsearch"></category></entry><entry><title>The conference island — DjangoCon Europe 2014</title><link href="https://markusholtermann.eu/2014/06/djangocon-europe-2014/" rel="alternate"></link><published>2014-06-05T00:00:00+02:00</published><updated>2014-06-05T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-06-05:/2014/06/djangocon-europe-2014/</id><summary type="html">&lt;p class="first last"&gt;About a month ago, I attended DjangoCon Europe on the Île des Embiez,
a small island between Toulon and Marseille. After an incredible circus
edition 2013 in Warsaw, this year&amp;#8217;s organizers stepped up to the plate to
host another amazing conference.&lt;/p&gt;
</summary><content type="html">&lt;img alt="DjangoCon 2014 -- Île des Embiez" class="margin-left align-right" src="/images/djangoconeu2014/logo.png" /&gt;
&lt;p&gt;It has been a while since I had the time to write another blog post. As you
might have noticed, I&amp;#8217;m an organizer of 2014&amp;#8217;s EuroPython in Berlin. My job as
the web team leader requires some responsibility I&amp;#8217;m eager to fulfill,
especially for a conference where we expect up to 1.200 attendees. Furthermore,
this job is a sideline to my regular job, being a computer science student.&lt;/p&gt;
&lt;p&gt;About a month ago, however, I took the time to attend the DjangoCon Europe.
After an incredible &lt;em&gt;circus edition&lt;/em&gt; 2013 in Warsaw, this year&amp;#8217;s organizers
stepped up to the plate to host another amazing conference. While everybody
knew DjangoCon Europe 2014 was going to be in France from the beginning, it
took some months until the announcement was made that the event would happen
somewhere on the Côte d&amp;#8217;Azur. The community, though, was kept in the dark about
the exact location for a few more months. Eventually the &lt;em&gt;Île des Embiez&lt;/em&gt;, a
small island between Toulon and Marseille, was announced and &lt;em&gt;djangoisland&lt;/em&gt;
born.&lt;/p&gt;
&lt;h2&gt;Getting there&lt;/h2&gt;
&lt;p&gt;Getting to this little island on Monday before official conference start was
kind of a challenge for me. Although I&amp;#8217;m living in Berlin, there are not many
(fast) ways to get to a bigger city in southern France. I took a flight via
Brussels in the early morning at about 7am and arrived in Marseille at about
12pm plus some waiting time at baggage claim. That&amp;#8217;s actually not that bad. But
getting from Marseille to this little island in the middle of nowhere was hard:&lt;/p&gt;
&lt;p&gt;From the Marseille Provence Airport (&lt;span class="caps"&gt;MRS&lt;/span&gt;) to Marseille Saint-Charles train
station by bus. Unfortunately the shuttle bus we could have taken if the plane
hasn&amp;#8217;t been delayed was already gone.&lt;/p&gt;
&lt;p&gt;From Saint-Charles we took a one hour train ride to Toulon train station. Sadly
we missed the connection bus there which would have taken us to the harbor in
La Brusc by a few minutes. But having about 2 hours free time in Toulon wasn&amp;#8217;t
that bad. We walked through the city to the harbor and grabbed some lunch.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/djangoconeu2014/dce01.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce01-315x315.jpg" alt="DjangoCon Europe 2014 -- Toulon" title="DjangoCon Europe 2014 -- Toulon"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce01-364x364.jpg" alt="DjangoCon Europe 2014 -- Toulon" title="DjangoCon Europe 2014 -- Toulon"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce01-480x480.jpg" alt="DjangoCon Europe 2014 -- Toulon" title="DjangoCon Europe 2014 -- Toulon"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/djangoconeu2014/dce02.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce02-315x315.jpg" alt="DjangoCon Europe 2014 -- Toulon" title="DjangoCon Europe 2014 -- Toulon"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce02-364x364.jpg" alt="DjangoCon Europe 2014 -- Toulon" title="DjangoCon Europe 2014 -- Toulon"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce02-480x480.jpg" alt="DjangoCon Europe 2014 -- Toulon" title="DjangoCon Europe 2014 -- Toulon"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/djangoconeu2014/dce03.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce03-315x315.jpg" alt="DjangoCon Europe 2014 -- Toulon harbor" title="DjangoCon Europe 2014 -- Toulon harbor"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce03-364x364.jpg" alt="DjangoCon Europe 2014 -- Toulon harbor" title="DjangoCon Europe 2014 -- Toulon harbor"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce03-480x480.jpg" alt="DjangoCon Europe 2014 -- Toulon harbor" title="DjangoCon Europe 2014 -- Toulon harbor"&gt;&lt;/a&gt;&lt;a class="span100 col s12 m6 l4" href="/images/djangoconeu2014/dce04.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce04-315x315.jpg" alt="DjangoCon Europe 2014 -- Toulon harbor" title="DjangoCon Europe 2014 -- Toulon harbor"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce04-364x364.jpg" alt="DjangoCon Europe 2014 -- Toulon harbor" title="DjangoCon Europe 2014 -- Toulon harbor"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce04-480x480.jpg" alt="DjangoCon Europe 2014 -- Toulon harbor" title="DjangoCon Europe 2014 -- Toulon harbor"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Back at the central bus station we met quite a lot other DjangoCon attendees
travelling with us to the port in La Brusc where the ferry brought us to the
island.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce05.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce05-315x315.jpg" alt="DjangoCon Europe 2014 -- La Brusc" title="DjangoCon Europe 2014 -- La Brusc"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce05-236x236.jpg" alt="DjangoCon Europe 2014 -- La Brusc" title="DjangoCon Europe 2014 -- La Brusc"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce05-480x480.jpg" alt="DjangoCon Europe 2014 -- La Brusc" title="DjangoCon Europe 2014 -- La Brusc"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce06.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce06-315x315.jpg" alt="DjangoCon Europe 2014 -- Ferry to the island" title="DjangoCon Europe 2014 -- Ferry to the island"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce06-236x236.jpg" alt="DjangoCon Europe 2014 -- Ferry to the island" title="DjangoCon Europe 2014 -- Ferry to the island"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce06-480x480.jpg" alt="DjangoCon Europe 2014 -- Ferry to the island" title="DjangoCon Europe 2014 -- Ferry to the island"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce07.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce07-315x315.jpg" alt="DjangoCon Europe 2014 -- Arrived on the Island" title="DjangoCon Europe 2014 -- Arrived on the Island"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce07-236x236.jpg" alt="DjangoCon Europe 2014 -- Arrived on the Island" title="DjangoCon Europe 2014 -- Arrived on the Island"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce07-480x480.jpg" alt="DjangoCon Europe 2014 -- Arrived on the Island" title="DjangoCon Europe 2014 -- Arrived on the Island"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;At about 6pm we arrived at the hotel. After I checked into my room I had an
amazing view from the balkony.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce08.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce08-315x315.jpg" alt="DjangoCon Europe 2014 -- Hotel entrance" title="DjangoCon Europe 2014 -- Hotel entrance"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce08-236x236.jpg" alt="DjangoCon Europe 2014 -- Hotel entrance" title="DjangoCon Europe 2014 -- Hotel entrance"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce08-480x480.jpg" alt="DjangoCon Europe 2014 -- Hotel entrance" title="DjangoCon Europe 2014 -- Hotel entrance"&gt;&lt;/a&gt;&lt;a class="span66 col s12 m4 l4" href="/images/djangoconeu2014/dce09.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce09-315x315.jpg" alt="DjangoCon Europe 2014 -- View from the hotel to the harbor" title="DjangoCon Europe 2014 -- View from the hotel to the harbor"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce09-236x236.jpg" alt="DjangoCon Europe 2014 -- View from the hotel to the harbor" title="DjangoCon Europe 2014 -- View from the hotel to the harbor"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce09-480x480.jpg" alt="DjangoCon Europe 2014 -- View from the hotel to the harbor" title="DjangoCon Europe 2014 -- View from the hotel to the harbor"&gt;&lt;/a&gt;&lt;a class="span100 col s12 m4 l4" href="/images/djangoconeu2014/dce10.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce10-315x315.jpg" alt="DjangoCon Europe 2014 -- View from the hotel to the harbor" title="DjangoCon Europe 2014 -- View from the hotel to the harbor"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce10-236x236.jpg" alt="DjangoCon Europe 2014 -- View from the hotel to the harbor" title="DjangoCon Europe 2014 -- View from the hotel to the harbor"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce10-480x480.jpg" alt="DjangoCon Europe 2014 -- View from the hotel to the harbor" title="DjangoCon Europe 2014 -- View from the hotel to the harbor"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;The Island&lt;/h2&gt;
&lt;blockquote&gt;
[The Île des Embiez] has a surface area of 0.9 square kilometres (0.35 sq
mi), with a coastline of 6 kilometres (3.7 mi). The island is 57 metres
(187 ft) high at its highest point. &lt;a class="citation-reference" href="#wiki" id="id1"&gt;[&lt;span class="caps"&gt;WIKI&lt;/span&gt;]&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;The sunny and warm weather animated me and many others to explore the island.
Here are some photos that capture some nice views.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce11.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce11-315x315.jpg" alt="DjangoCon Europe 2014 -- Harbor on the island" title="DjangoCon Europe 2014 -- Harbor on the island"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce11-236x236.jpg" alt="DjangoCon Europe 2014 -- Harbor on the island" title="DjangoCon Europe 2014 -- Harbor on the island"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce11-480x480.jpg" alt="DjangoCon Europe 2014 -- Harbor on the island" title="DjangoCon Europe 2014 -- Harbor on the island"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce12.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce12-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce12-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce12-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce13.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce13-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce13-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce13-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="span100 col s12 m4 l4" href="/images/djangoconeu2014/dce14.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce14-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce14-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce14-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce15.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce15-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce15-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce15-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce16.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce16-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce16-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce16-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="noclear col s12 m4 l4" href="/images/djangoconeu2014/dce17.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce17-315x315.jpg" alt="DjangoCon Europe 2014 -- Be aware of the goats!" title="DjangoCon Europe 2014 -- Be aware of the goats!"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce17-236x236.jpg" alt="DjangoCon Europe 2014 -- Be aware of the goats!" title="DjangoCon Europe 2014 -- Be aware of the goats!"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce17-480x480.jpg" alt="DjangoCon Europe 2014 -- Be aware of the goats!" title="DjangoCon Europe 2014 -- Be aware of the goats!"&gt;&lt;/a&gt;&lt;a class="span100 col s12 m4 l4" href="/images/djangoconeu2014/dce18.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce18-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce18-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce18-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce19.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce19-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce19-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce19-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="noclear col s12 m4 l4" href="/images/djangoconeu2014/dce20.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce20-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce20-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce20-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce21.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce21-315x315.jpg" alt="DjangoCon Europe 2014 -- Picnic" title="DjangoCon Europe 2014 -- Picnic"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce21-236x236.jpg" alt="DjangoCon Europe 2014 -- Picnic" title="DjangoCon Europe 2014 -- Picnic"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce21-480x480.jpg" alt="DjangoCon Europe 2014 -- Picnic" title="DjangoCon Europe 2014 -- Picnic"&gt;&lt;/a&gt;&lt;a class="span100 col s12 m4 l4" href="/images/djangoconeu2014/dce22.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce22-315x315.jpg" alt="DjangoCon Europe 2014 -- The highest point on the island" title="DjangoCon Europe 2014 -- The highest point on the island"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce22-236x236.jpg" alt="DjangoCon Europe 2014 -- The highest point on the island" title="DjangoCon Europe 2014 -- The highest point on the island"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce22-480x480.jpg" alt="DjangoCon Europe 2014 -- The highest point on the island" title="DjangoCon Europe 2014 -- The highest point on the island"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce23.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce23-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce23-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce23-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce24.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce24-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce24-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce24-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;a class="col s12 m4 l4" href="/images/djangoconeu2014/dce25.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/djangoconeu2014/dce25-315x315.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/djangoconeu2014/dce25-236x236.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;img class="small lazyload" data-src="/images/thumb/djangoconeu2014/dce25-480x480.jpg" alt="DjangoCon Europe 2014" title="DjangoCon Europe 2014"&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;The Conference&lt;/h2&gt;
&lt;p&gt;There were many talks spanning all kinds of topics, database related topics
next to front-end recommendations. Explaining where to look for performance
optimizations and why Django is not necessarily a monolithic web-framework.
The complexity covered by those talks varied quite a log. There were talks for
beginners giving hints on how to work with &lt;em&gt;generic classed based views&lt;/em&gt; and
talks for experts on what one might experience when ripping out some parts of
Django.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=LHqEKpcA6Mo"&gt;Advanced Web Typography&lt;/a&gt; by &lt;em&gt;Idan Gazit&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=aFRH-oHcbn8"&gt;Django minus Django&lt;/a&gt; by &lt;em&gt;Jacob Burch&lt;/em&gt; &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; &lt;em&gt;Jacob Kaplan Moss&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=9IxW1Atq7UI"&gt;Frequently missed performance gains&lt;/a&gt; by &lt;em&gt;Frank Wiles&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=CRkYgtHl4KU"&gt;From __icontains to search&lt;/a&gt; by &lt;em&gt;Honza Kral&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=Qr-MBO-E0IY"&gt;Good schema design - and why it matters!&lt;/a&gt; by &lt;em&gt;Andrew Godwin&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=59WA_yNgVn8"&gt;Introduction to docker&lt;/a&gt; by &lt;em&gt;Amjith Ramanujam&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=rZa9vbTp9tw"&gt;Really, Really Fast Django&lt;/a&gt; by &lt;em&gt;Christophe Pettus&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=0eDbDpZsWxc"&gt;The future of PostgreSQL in Django&lt;/a&gt; by &lt;em&gt;Marc Tamlyn&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=o7edMNyZCZw"&gt;Where the wild things are&lt;/a&gt; by &lt;em&gt;Aymeric Augustin&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;It was an amazing conference and I&amp;#8217;m glad I was able to attend. I met many
people from the community. Having interesting discussions on all kind of
topics, both Django and not Django related.&lt;/p&gt;
&lt;p&gt;As highlight at the end of the conference, my pull-request for &lt;a class="reference external" href="https://code.djangoproject.com/ticket/21905"&gt;#21905&lt;/a&gt; got
&lt;a class="reference external" href="https://github.com/django/django/commit/9d8c73f6a1c636853a5c5013f21985d702b2301b"&gt;merged&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;License&lt;/h2&gt;
&lt;div class="figure align-right"&gt;
&lt;a class="reference external image-reference" href="http://creativecommons.org/licenses/by-sa/4.0/"&gt;&lt;img alt="Creative Commons License" src="/images/cc-by-sa-3.0-88x31.png" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;The photos in this blog post are licensed under a &lt;a class="reference external" href="http://creativecommons.org/licenses/by-sa/4.0/"&gt;Creative Commons
Attribution-ShareAlike 4.0 International License.&lt;/a&gt;.&lt;/p&gt;
&lt;div class="legend"&gt;
The DjangoCon 2014 logo is from &lt;a class="reference external" href="http://2014.djangocon.eu/"&gt;http://2014.djangocon.eu/&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;table class="docutils citation" frame="void" id="wiki" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[Wiki]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Embiez"&gt;http://en.wikipedia.org/wiki/Embiez&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Misc"></category><category term="Community"></category><category term="Django"></category><category term="DjangoCon"></category></entry><entry><title>EuroPython website sprint a success - many new features implemented</title><link href="https://markusholtermann.eu/2014/03/europython-website-sprint-a-success-many-new-features/" rel="alternate"></link><published>2014-03-30T00:00:00+01:00</published><updated>2014-03-30T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2014-03-30:/2014/03/europython-website-sprint-a-success-many-new-features/</id><summary type="html">&lt;p class="first last"&gt;Last weekend the EuroPython web team met in Berlin in order to bring
the EuroPython 2014 website software forward.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Last weekend the web team &lt;a class="reference external" href="http://www.meetup.com/Python-Users-Berlin-PUB/events/168403892/"&gt;met&lt;/a&gt; in Berlin (and was supported by one member
remotely) in the rooms of Veit Schiele Communications GmbH in order to bring the
&lt;a class="reference external" href="https://github.com/EuroPython/djep"&gt;EuroPython 2014 website software&lt;/a&gt; forward. The team under the leadership of
Markus Holtermann implemented the following new features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A short while ago we made some heavy changes to the main menu. Unfortunately
the user experience wasn’t the way we expected it to be. Thus we got some
comments on the EuroPython &lt;a class="reference external" href="https://mail.python.org/pipermail/europython/2014-February/008323.html"&gt;mailing list&lt;/a&gt;. It took us some time to decide how
to change the way the menu should work. Eventually, a click on a main menu
item only toggles the menu as of now (&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/103"&gt;GitHub issue #103&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;We want to generate the badges (and other stuff for the conference) directly
from the user data you provide on the website. This presented us with some
kind of challenge as you will see in the next bullet point, too. We decided
that the singe point of truth for the user data is the user profile. We
therefore added a field where you can enter your interests. (&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/105"&gt;GitHub issue
#105&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;As said before, the single point of truth for the user data is and will be the
user profile. Unfortunately, during the purchase process of a ticket, we only
have information about the buyer, not any of the intended ticket users.
Although we didn’t change this part in the update (and we won’t change it in
the future), you are now able to assign a ticket to another user. This is
incredible useful for those buyers ordering multiple tickets, eg. for their
colleagues or friends. To assign the ticket to somebody else, ask him/her for
the user name and assign the ticket to him/her from &lt;a class="reference external" href="https://ep2014.europython.eu/en/tickets/mine/"&gt;your purchase view&lt;/a&gt;. If
the intended user doesn’t have a account yet, ask him to create one. All
tickets that are &lt;em&gt;not&lt;/em&gt; assigned to somebody will use the first and last name
given during purchase. (&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/101"&gt;GitHub issue #101&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Since many attendees are not from Germany, we looked for a way to hand out &lt;span class="caps"&gt;SIM&lt;/span&gt;
cards. Due to legal restrictions this is not &lt;em&gt;that&lt;/em&gt; simple in Germany (you
normally need to present a photo &lt;span class="caps"&gt;ID&lt;/span&gt;). Fortunately we found a reseller where we
become some kind of reseller ourselves (it&amp;#8217;s a bit more complicated than that
;) ). Hence we will offer &lt;span class="caps"&gt;SIM&lt;/span&gt; cards within the next days that you can buy
beforehand and you can pick up during check-in (more details soon). (&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/100"&gt;GitHub
issue #100&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;When you modify your profile and try to upload a new avatar, but there are
errors in some other fields, the avatar gets lost. We solve this by adding
front-end validation to this and many other forms. (&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/47"&gt;GitHub issue #47&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;If you are looking for a job (in your real live, not the one on the internet
:D), or if you think about getting a new job for whatever reason, you can now
opt-in (and later opt-out) of job offers by our sponsors. We will &lt;strong&gt;not&lt;/strong&gt; hand
over any of your data to the sponsors. The sponsors have to give us their
offer and we will send it only to those users interested in job offers.
(&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/78"&gt;GitHub issue #78&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The list of your purchases now shows canceled purchases too. (&lt;a class="reference external" href="https://github.com/EuroPython/djep/issues/102"&gt;GitHub issue
#102&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from those changes interesting to you as an attendee, loads of other
changes made it into production. If you are interested, have a look at our
repository on GitHub: &lt;a class="reference external" href="https://github.com/EuroPython/djep"&gt;https://github.com/EuroPython/djep&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All new features went into production today with an update of the portal
software.&lt;/p&gt;
&lt;p&gt;Thanks a lot to the web team and all other sprinters for their dedicated work on
the EuroPython 2014 web software.&lt;/p&gt;
&lt;p&gt;Source: &lt;a class="reference external" href="http://blog.europython.eu/post/81187947812"&gt;blog.europython.eu&lt;/a&gt;, Mar 30, 2014, 20:30pm &lt;span class="caps"&gt;CEST&lt;/span&gt;&lt;/p&gt;
</content><category term="Misc"></category><category term="Community"></category><category term="Django"></category><category term="EuroPython"></category><category term="Sprint"></category></entry><entry><title>EuroPython 2014 Conference in Berlin at the bcc Berlin Congress Center</title><link href="https://markusholtermann.eu/2013/12/europython-2014-conference-in-berlin-at-the-bcc-berlin-congress-center/" rel="alternate"></link><published>2013-12-24T00:00:00+01:00</published><updated>2013-12-24T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2013-12-24:/2013/12/europython-2014-conference-in-berlin-at-the-bcc-berlin-congress-center/</id><summary type="html">&lt;p class="first last"&gt;Organizers expect up to 1,200 attendees&lt;/p&gt;
</summary><content type="html">&lt;img alt="EuroPython 2014, July 21-27, Berlin" class="align-right" src="/images/ep14-logo-250x.png" /&gt;
&lt;p&gt;The next EuroPython conference will be held from July 21 - 27, 2014 at the bcc
Berlin Congress Center in the heart of Berlin. The EuroPython conference is the
second largest global conference for the popular programming language Python,
after the PyCon &lt;span class="caps"&gt;US&lt;/span&gt; conference in North America. The bcc is located at the
central Alexander Square. It offers ideal facilities for a developer conference
on 3,000 m2. The organizers are expecting up to 1,200 participants, continuing
the growth of the EuroPython conference series. The very successful EuroPython
this year attracted almost 900 Python enthusiasts. It was hosted in Florence,
Italy for the third time running.&lt;/p&gt;
&lt;p&gt;Berlin offers ideal conditions for a large international developer conference.
The city with its about 2,500 young businesses has a large and fast-growing
start-up scene. 300 universities, polytechnics, research institutes and
technology parks underline Berlin&amp;#8217;s importance as one of Europe&amp;#8217;s leading
science and research locations. Thanks to its rich cultural landscape with
around 180 museums, renowned international art collections and precious works
of art as well as a lively art and music scene, Berlin is one of the most
vibrant and vividly creative metropolitan centers in Europe. Its attractions
and excellent national and international transport links have already put
Berlin among the top three in the international conference and congress region
rankings.&lt;/p&gt;
&lt;p&gt;The conference will be organized locally by the Python Software Verband e.V.,
the Python Users Berlin, the Django User Group Berlin and the PyLadies Berlin,
in cooperation with the EuroPython Society for the Python community. With five
lecture tracks and two tutorial tracks running in parallel, the EuroPython
conference offers an attractive schedule for all attendees.&lt;/p&gt;
&lt;p&gt;The EuroPython 2014 logo expresses what is on the organizer&amp;#8217;s minds, which is
getting people from very different areas of expertise together to communicate.
Python is used as a programming language in industrial and service enterprises
as well as in research and education. The EuroPython brings together developers
who work together on fascinating tasks and as a result have found unique
solutions to their problems.&lt;/p&gt;
&lt;h2&gt;About Python&lt;/h2&gt;
&lt;p&gt;Python is an interpreted high-level language. In the &lt;span class="caps"&gt;TIOBE&lt;/span&gt; index Python has
featured among the top 10 of favorite programming languages for years. Python
is ideal for development. It is a lean language that is easy to learn for both
developers and users. The language was created in the late 1980s by Guido van
Rossum. Today, the language is developed by a large open source community and
backed by the Python Software Foundation (&lt;span class="caps"&gt;PSF&lt;/span&gt;), which owns the copyright to
Python.&lt;/p&gt;
&lt;h2&gt;About the Python Software Verband e.V.&lt;/h2&gt;
&lt;p&gt;The Python Software Verband e.V. represents the interests of the
German-speaking Python community. It is a German non-profit organization that
focuses on promoting the Python programming language and supports activities of
different Python interests groups. Its members are involved in a wide range of
Python activities including Python core development and many Open Source
projects.&lt;/p&gt;
&lt;h2&gt;About the EuroPython Society&lt;/h2&gt;
&lt;p&gt;The EuroPython Society (&lt;span class="caps"&gt;EPS&lt;/span&gt;) is a Swedish non-profit organization which holds
the rights to the EuroPython conference series and trademarks. It coordinates
with local organizers to run EuroPython conferences. The &lt;span class="caps"&gt;EPS&lt;/span&gt; was formed in 2004
with the mission to turn EuroPython into a successful Python conference series
for the years to come.&lt;/p&gt;
&lt;h2&gt;About the Python Users Berlin&lt;/h2&gt;
&lt;p&gt;Founded in 2002, Python Users Berlin holds monthly meetings for presentations
and talks about all facets of the Python programming language. Python Users
Berlin currently has around 330 members.&lt;/p&gt;
&lt;h2&gt;About the Django User Group Berlin&lt;/h2&gt;
&lt;p&gt;The Django User Group Berlin was founded in 2009. Its approximately 130 members
meet monthly for talks related to the Python web framework Django.&lt;/p&gt;
&lt;h2&gt;About the PyLadies Berlin&lt;/h2&gt;
&lt;p&gt;PyLadies is an international movement that motivates and supports women to
engage with and take an active role within the Python community as developers.
PyLadies Berlin has around 100 members.&lt;/p&gt;
&lt;p&gt;Source: &lt;a class="reference external" href="https://ep2014.europython.eu/en/conference/press-releases/europython-2014-at-bcc-berlin/"&gt;ep2014.europython.eu&lt;/a&gt;, Dec 12, 2013, 11:30pm &lt;span class="caps"&gt;CET&lt;/span&gt;&lt;/p&gt;
</content><category term="Misc"></category><category term="Community"></category><category term="Django"></category><category term="EuroPython"></category><category term="PyPy"></category><category term="Python"></category></entry><entry><title>EuroPython 2014 im bcc Berlin Congress Center</title><link href="https://markusholtermann.eu/de/2013/12/europython-2014-conference-in-berlin-at-the-bcc-berlin-congress-center/" rel="alternate"></link><published>2013-12-24T00:00:00+01:00</published><updated>2013-12-24T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2013-12-24:/de/2013/12/europython-2014-conference-in-berlin-at-the-bcc-berlin-congress-center/</id><summary type="html">&lt;p class="first last"&gt;Veranstalter rechnen mit bis zu 1200 Besuchern&lt;/p&gt;
</summary><content type="html">&lt;img alt="EuroPython 2014, July 21-27, Berlin" class="align-right" src="/images/ep14-logo-250x.png" /&gt;
&lt;p&gt;Die nächste EuroPython wird vom 21. bis 27. Juli 2014 im bcc Berlin Congress
Center mitten in Berlin stattfinden. Die EuroPython ist nach der PyCon &lt;span class="caps"&gt;US&lt;/span&gt;
weltweit die zweitgrößte Konferenz für die beliebte Programmiersprache. Das bcc
am Alexanderplatz bietet mit seinen 3000 Quadratmetern ideale Voraussetzungen
für eine Entwicklerkonferenz. Die Veranstalter rechnen mit bis zu 1200
Teilnehmern, entsprechend der Wachstumsrate dieser Konferenzreihe. Die bereits
sehr erfolgreiche EuroPython hat dieses Jahr 900 Python Enthusiasten
zusammengeführt. Sie fand zum dritten Mal in Florenz, Italien statt.&lt;/p&gt;
&lt;p&gt;Berlin bietet ideale Bedingungen für eine große internationale
Entwicklerkonferenz. Die Stadt hat mit rund 2500 jungen Unternehmen eine große
und schnell wachsende Startup-Szene. Rund 300 Universitäten, Fachhochschulen,
Forschungsinstitute und Technologieparks machen Berlin zu einem der wichtigsten
Wissenschafts- und Forschungsstandorte in Europa. Dank seiner reichhaltigen
Kulturlandschaft mit rund 180 Museen, die bedeutende internationale Sammlungen
und Kunstschätze beherbergen, sowie seiner lebendigen Kunst- und Musikszene
gilt Berlin als eine der pulsierendsten und kreativsten Metropolen in Europa.
Die Attraktivtät der Stadt und ihre hervorragende nationale und internationale
Verkehrsanbindung hat Berlin auf Platz 3 der internationalen Tagungs- und
Kongressmetropolen katapultiert.&lt;/p&gt;
&lt;p&gt;Die Konferenz wird vor Ort vom Python Software Verband e.V., von den Python
Users Berlin, der Django User Group Berlin und den PyLadies Berlin organisiert.
Mit fünf Vortrags- und zwei parallelen Tutorial-Tracks bietet die EuroPython
ihren Besuchern ein prall gefülltes Programm.&lt;/p&gt;
&lt;p&gt;Das Signet der EuroPython 2014 zeigt, worum es den Veranstaltern geht. Sie
wollen Menschen aus ganz unterschiedlichen Bereichen miteinander ins Gespräch
bringen. Python wird als Programmiersprache in Industrie- und
Dienstleistungsunternehmen sowie in der Forschung und Lehre eingesetzt. Auf der
EuroPython kommen Entwickler zusammen, die an faszinierenden Aufgabenstellungen
arbeiten und dabei einzigartige Lösungen gefunden haben.&lt;/p&gt;
&lt;h2&gt;Über Python&lt;/h2&gt;
&lt;p&gt;Python ist eine interpretierte höhere Programmiersprache. Sie rangiert im
&lt;span class="caps"&gt;TIOBE&lt;/span&gt;-Index seit Jahren unter den zehn beliebtesten Programmiersprachen
weltweit. Python eignet sich ideal für agile Entwicklung. Es handelt sich um
eine schlanke, leicht zu lernende Programmiersprache, die die Grenzen zwischen
Anwendern und Entwicklern verwischt. Die Sprache wurde in den späten achtzigern
von Guido van Rossum entwickelt. Heute wird die Sprache von einer großen
Open-Source-Community weiterentwickelt und von der Python Software Foundation
(&lt;span class="caps"&gt;PSF&lt;/span&gt;) abgesichert, die die Namensrechte an der Marke Python hält.&lt;/p&gt;
&lt;h2&gt;Über den Python Software Verband e.V.&lt;/h2&gt;
&lt;p&gt;Der Python Software Verband e.V. vertritt die Interessen der deutschsprachigen
Python Community. Er ist eine Non-Profit Organisation nach deutschem Recht,
deren Fokus auf die Förderung der Programmiersprache Python und die
Unterstützung der Aktivitäten verschiedenster Interessen-Gruppen rund um Python
ausgerichtet ist. Seine Mitglieder sind an einer breiten Palette von
Aktivitäten inklusive Python Kernentwicklung und vielen Open-Source-Projekten
beteiligt.&lt;/p&gt;
&lt;h2&gt;Über die EuroPython Society&lt;/h2&gt;
&lt;p&gt;Die EuroPython Society (&lt;span class="caps"&gt;EPS&lt;/span&gt;) ist eine schwedische gemeinnützige Organisation,
welche die Rechte an der Europython Konferenz Serie und der Marke hält. Sie
koordiniert gemeinsam mit der lokalen Organisation die Ausführung der
Europython Konferenzen. Die EuroPython Society wurde 2004 gegründet, um in den
darauf folgenden Jahren eine erfolgreiche Python Konferenz zu gewährleisten.&lt;/p&gt;
&lt;h2&gt;Über die Python Users Berlin&lt;/h2&gt;
&lt;p&gt;Die Python Users Berlin gründeten sich 2002. Die Gruppe trifft sich monatlich
zu Vorträgen und Diskussionen zu Themen rund um die Programmiersprache Python.
Zurzeit haben die Python Users Berlin rund 330 Mitglieder.&lt;/p&gt;
&lt;h2&gt;Über die Django User Group Berlin&lt;/h2&gt;
&lt;p&gt;Die Django User Group Berlin wurde 2009 gegründet. Die rund 130
Mitgliedertreffen sich im Monatsrhythmus zu Vorträgen rund um das
Python-Web-Framework Django.&lt;/p&gt;
&lt;h2&gt;Über die PyLadies Berlin&lt;/h2&gt;
&lt;p&gt;Die PyLadies bilden eine internationale Bewegung, die Frauen motiviert und
unterstützt, innerhalb der Python-Community als Entwicklerinnen in führenden
Positionen aktiv zu werden. Die PyLadies Berlin haben zurzeit rund 100
Mitglieder.&lt;/p&gt;
&lt;p&gt;Quelle: &lt;a class="reference external" href="https://ep2014.europython.eu/en/conference/press-releases/europython-2014-at-bcc-berlin/"&gt;ep2014.europython.eu&lt;/a&gt;, 12. Dez 2013, 23:30 &lt;span class="caps"&gt;CET&lt;/span&gt;&lt;/p&gt;
</content><category term="Misc"></category><category term="Community"></category><category term="Django"></category><category term="EuroPython"></category><category term="PyPy"></category><category term="Python"></category></entry><entry><title>bakery — Where cookies are made!</title><link href="https://markusholtermann.eu/2013/10/bakery-where-cookies-are-made/" rel="alternate"></link><published>2013-10-01T00:00:00+02:00</published><updated>2013-10-01T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2013-10-01:/2013/10/bakery-where-cookies-are-made/</id><summary type="html">&lt;p class="first last"&gt;Djangodash is a coding contest where teams have 48 hours time to
start and sprint a project. We worked on a website to host cookiecutter
projects.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;It has been some time since my last blog post. So here&amp;#8217;s an update. This is
about a great event I participated in last weekend, the &lt;a class="reference external" href="http://djangodash.com/"&gt;djangodash 2013&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The djangodash is a coding contest where teams have 48 hours time to start and
sprint a project. As this dash is called &lt;em&gt;djangodash&lt;/em&gt;, the Django must be used.
Furthermore the teams are limited to at most 3 members and a couple of other
&lt;a class="reference external" href="http://djangodash.com/rules/"&gt;rules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, what project were my friend and I working on? It&amp;#8217;s called &lt;em&gt;bakery&lt;/em&gt;. You can
find it on &lt;a class="reference external" href="https://github.com/muffins-on-dope/bakery"&gt;github&lt;/a&gt; as well as a live version at
&lt;a class="reference external" href="http://djangodash2013.webshox.org/"&gt;djangodash2013.webshox.org&lt;/a&gt;. But what does &lt;em&gt;bakery&lt;/em&gt; actually do and what is
it?  In simplified terms &lt;em&gt;bakery&lt;/em&gt; is an index for &lt;a class="reference external" href="https://github.com/audreyr/cookiecutter"&gt;cookiecutter&lt;/a&gt; templates. In
longer terms, &lt;em&gt;bakery&lt;/em&gt; offers an &lt;span class="caps"&gt;API&lt;/span&gt; to create, find, modify, update and share
cookiecutter templates at a central point to clear up the mess with manually
updated readme files providing an incomplete list of templates. Additionally
&lt;em&gt;bakery&lt;/em&gt; offers some neat features such as forking a template to your own
github account or voting a template up.  We are working on many more planned
features, e.g. automatically updating the index, directly editing the
cookiecutter.json file on the index.&lt;/p&gt;
&lt;p&gt;So, what are these &lt;em&gt;cookiecutter&lt;/em&gt; templates and what is &lt;em&gt;cookiecutter&lt;/em&gt;?
&lt;em&gt;cookiecutter&lt;/em&gt; itself is a tool that, given a project template, bootstraps a
project with its directory structure and files (including their content) based
on a set of context variables. To see what these templates (aka cookiecutters)
look like, just have a look at the &lt;a class="reference external" href="https://github.com/audreyr/cookiecutter#available-cookiecutters"&gt;list of available cookiecutters&lt;/a&gt;.&lt;/p&gt;
</content><category term="Development"></category><category term="Django"></category><category term="Git"></category><category term="Python"></category></entry><entry><title>Showing BVG Departures In Your Office</title><link href="https://markusholtermann.eu/2013/06/showing-bvg-departures-in-your-office/" rel="alternate"></link><published>2013-06-14T00:00:00+02:00</published><updated>2013-06-14T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2013-06-14:/2013/06/showing-bvg-departures-in-your-office/</id><summary type="html">&lt;p class="first last"&gt;A small Python application to fetch departures from the &lt;span class="caps"&gt;BVG&lt;/span&gt; website.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Yesterday evening I gave a lightning talk at the &lt;a class="reference external" href="http://www.meetup.com/Python-Users-Berlin-PUB/events/105128552/"&gt;Python User Group Berlin&lt;/a&gt;
called &amp;#8220;&lt;em&gt;Showing &lt;span class="caps"&gt;BVG&lt;/span&gt; Departures In Your Office&lt;/em&gt;&amp;#8220;. You can download the slides
&lt;a class="reference external" href="https://speakerdeck.com/markush/showing-bvg-departures-in-your-office"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The whole idea to the project came up when our student organization
(&lt;a class="reference external" href="http://freitagsrunde.org"&gt;Freitagsrunde&lt;/a&gt;) had to relocate to a new building at the end of 2012.&lt;/p&gt;
&lt;p&gt;Since our new student organization meeting room is quite near to the next bus
stop (bee-line), but the actual distance is way longer and we have to cross
several doors, waiting in the room and heading to the bus stop when the bus
arrives is not going to work.&lt;/p&gt;
&lt;p&gt;As computer scientists we had to come up with a solution to somehow grab the
departures of the bus stop and show them in the room.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l6" href="/images/bvg-grabber-lightningtalk1.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/bvg-grabber-lightningtalk1-483x483.jpg" alt="bvg-grabber in use" title="bvg-grabber in use"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/bvg-grabber-lightningtalk1-364x364.jpg" alt="bvg-grabber in use" title="bvg-grabber in use"&gt;&lt;img class="small lazyload" data-src="/images/thumb/bvg-grabber-lightningtalk1-480x480.jpg" alt="bvg-grabber in use" title="bvg-grabber in use"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l6" href="/images/bvg-grabber-lightningtalk2.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/bvg-grabber-lightningtalk2-483x483.jpg" alt="bvg-grabber in use" title="bvg-grabber in use"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/bvg-grabber-lightningtalk2-364x364.jpg" alt="bvg-grabber in use" title="bvg-grabber in use"&gt;&lt;img class="small lazyload" data-src="/images/thumb/bvg-grabber-lightningtalk2-480x480.jpg" alt="bvg-grabber in use" title="bvg-grabber in use"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;At first the information retrieval was hard-coded and it was only possible to
get the departures for a single bus stop. After a short time we got the feature
request to also display the departures of nearby subways and other public
transport. We ended up rewriting the entire application and created a library
we then called &lt;a class="reference external" href="https://github.com/MarkusH/bvg-grabber"&gt;bvg-grabber&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The quite simple &lt;span class="caps"&gt;API&lt;/span&gt; allows us to use both information provided by the &lt;a class="reference external" href="http://bvg.de"&gt;&lt;span class="caps"&gt;BVG&lt;/span&gt;&lt;/a&gt;:
the actual departures which, in that days, only returned departures about
busses, and the scheduled departures as they are written on the time tables:&lt;/p&gt;
&lt;p&gt;A class inheriting from &lt;tt class="docutils literal"&gt;QueryApi&lt;/tt&gt; must implement a &lt;tt class="docutils literal"&gt;call()&lt;/tt&gt; function that
returns a &lt;tt class="docutils literal"&gt;Response&lt;/tt&gt; object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QueryApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Performs the requests to the data source&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Needs to return a Response&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;The inheriting class needs to &amp;quot;&lt;/span&gt;
                                  &lt;span class="s2"&gt;&amp;quot;implement the call() method!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;tt class="docutils literal"&gt;Response&lt;/tt&gt; object must contain a &lt;tt class="docutils literal"&gt;state&lt;/tt&gt; (True/False for success/failed
request) and, for a successful request, the departing station and a list of
Departures, or, for a failed request, the error that occurred.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Returned by a QueryApi, contains list of Departures&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;departures&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_departures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;station&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;departures&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;departures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_departures&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, a &lt;tt class="docutils literal"&gt;Departure&lt;/tt&gt; defines &lt;em&gt;when&lt;/em&gt; a certain &lt;em&gt;line&lt;/em&gt; leaves at a certain
&lt;em&gt;start&lt;/em&gt; station and at which station the ride will &lt;em&gt;end&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@total_ordering&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Departure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Start and end station, next departure time&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;no_add_day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Some magic happens here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As we are using the actual and the scheduled information from the &lt;span class="caps"&gt;BVG&lt;/span&gt; as you
can see above, here are 2 short examples how to use them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bvggrabber.api.actualdeparture&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ActualDepartureQueryApi&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ActualDepartureQueryApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Ernst-Reuter-Platz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departures&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ernst-Reuter-Platz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;Zoologischer&lt;/span&gt; &lt;span class="n"&gt;Garten&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt; &lt;span class="mi"&gt;245&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;Zoologischer&lt;/span&gt; &lt;span class="n"&gt;Garten&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt; &lt;span class="n"&gt;X9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;Zoologischer&lt;/span&gt; &lt;span class="n"&gt;Garten&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt; &lt;span class="n"&gt;M45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Johannesstift&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt; &lt;span class="n"&gt;M45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Flughafen&lt;/span&gt; &lt;span class="n"&gt;Tegel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt; &lt;span class="n"&gt;X9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Johannesstift&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt; &lt;span class="n"&gt;M45&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bvggrabber.api.scheduleddeparture&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ScheduledDepartureQueryApi&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ScheduledDepartureQueryApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Ernst-Reuter-Platz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;departures&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ernst-Reuter-Platz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Johannesstift&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Berlin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt;  &lt;span class="n"&gt;M45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hertzallee&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Berlin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt;  &lt;span class="mi"&gt;245&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;Pankow&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Berlin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;U2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hertzallee&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Berlin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bus&lt;/span&gt;  &lt;span class="n"&gt;M45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ernst&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Reuter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;Theodor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Heuss&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Platz&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Berlin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;U2&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#8217;m looking forward to your ideas and feature requests.&lt;/p&gt;
&lt;p&gt;Markus&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/markush/showing-bvg-departures-in-your-office"&gt;Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/MarkusH/bvg-grabber"&gt;bvg-grabber on github.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Git"></category><category term="Linux"></category><category term="Python"></category><category term="Studium"></category></entry><entry><title>Mirroring gitolite</title><link href="https://markusholtermann.eu/2013/02/mirroring-gitolite/" rel="alternate"></link><published>2013-02-18T00:00:00+01:00</published><updated>2013-02-18T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2013-02-18:/2013/02/mirroring-gitolite/</id><summary type="html">&lt;p class="first last"&gt;A long time ago I wrote an article about how to install gitolite.
Yesterday an article popped up in my feed reader that shows a short script
on how to mirroring gitolite servers.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A long time ago I wrote an article about &lt;a class="reference external" href="https://markusholtermann.eu/2011/09/my-own-git-server-installing-gitolite/"&gt;how to install gitolite&lt;/a&gt;. Yesterday
an article popped up in my feed reader that shows a short script on how to
&lt;a class="reference external" href="http://noqqe.de/blog/2013/02/17/mirroring-gitolite/"&gt;mirroring gitolite&lt;/a&gt; servers.&lt;/p&gt;
&lt;p&gt;Since I wrote my custom script once I had set up gitolite, I was a bit curious
about the differences to my script today. So I checked the referred script and
noticed some problems that probably occur over the time:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;New branches on the remote are not fetched&lt;/li&gt;
&lt;li&gt;New tags on the remote are not fetched&lt;/li&gt;
&lt;li&gt;Using working copies results in way larger mirrors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As one can see, the script lacks some major features.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c1"&gt;# Copyright (c) 2011-2013 Markus Holtermann&lt;/span&gt;
&lt;span class="c1"&gt;# MIT License: http://download.markusholtermann.eu/MIT.txt&lt;/span&gt;

&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/data/git-mirror/
&lt;span class="nv"&gt;REMOTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;git@git.example.com

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; ! -d &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    mkdir -p &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; repo &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;ssh &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REMOTE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&amp;gt; /dev/null &lt;span class="p"&gt;|&lt;/span&gt; grep -e &lt;span class="s2"&gt;&amp;quot;^\s\+R&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed -e &lt;span class="s1"&gt;&amp;#39;s/\r//g&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $3}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; -d &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.git &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.git
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Updating &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REMOTE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ...&amp;quot;&lt;/span&gt;
        git fetch --all
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; ... done&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;cd&lt;/span&gt; -
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Cloning &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REMOTE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ...&amp;quot;&lt;/span&gt;
        git clone --mirror &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REMOTE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.git
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; ... done&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The magic to prevent working directories lies in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--mirror&lt;/span&gt;&lt;/tt&gt; parameter
while cloning a repository. To get notified about new branches and tags on the
remote side, I use &lt;tt class="docutils literal"&gt;git fetch &lt;span class="pre"&gt;--all&lt;/span&gt;&lt;/tt&gt;. The &lt;a class="reference external" href="http://linux.die.net/man/1/git-fetch"&gt;man (1) page to git-fetch&lt;/a&gt;
describes it as&lt;/p&gt;
&lt;blockquote&gt;
Fetches named heads or tags from one or more other repositories, along with
the objects necessary to complete them. [&amp;#8230;]&lt;/blockquote&gt;
&lt;p&gt;However, the script above does not remove branches/tags that don&amp;#8217;t exist on the
remote any more. Since I use the script only for backup purposes, I don&amp;#8217;t need
that feature. But I would just remove all branches (local and remote) from the
local mirror and would run git-fetch afterwards.&lt;/p&gt;
&lt;p&gt;Enjoy the script.&lt;/p&gt;
</content><category term="Linux"></category><category term="Bash"></category><category term="Git"></category><category term="Network"></category><category term="Server"></category></entry><entry><title>Pictures from NYC</title><link href="https://markusholtermann.eu/2013/01/pictures-from-nyc/" rel="alternate"></link><published>2013-01-15T00:00:00+01:00</published><updated>2013-01-15T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2013-01-15:/2013/01/pictures-from-nyc/</id><summary type="html">&lt;p class="first last"&gt;Some of my favorite pictures from my last trip to New York City in
summer 2009.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Yesterday a friend of mine asked around if someone has ever been in New York
City. I replied and promised him to show some of my favorite pictures from my
last trip to &lt;span class="caps"&gt;NYC&lt;/span&gt; in summer 2009. Looking through the more than 1000 photos from
those days, I picked the following 8.&lt;/p&gt;
&lt;p&gt;Enjoy them!&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc1.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc1-315x315.jpg" alt="View from the Empire State Building to Midtown Manhattan and Central Park" title="View from the Empire State Building to Midtown Manhattan and Central Park"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc1-364x364.jpg" alt="View from the Empire State Building to Midtown Manhattan and Central Park" title="View from the Empire State Building to Midtown Manhattan and Central Park"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc1-480x480.jpg" alt="View from the Empire State Building to Midtown Manhattan and Central Park" title="View from the Empire State Building to Midtown Manhattan and Central Park"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc2.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc2-315x315.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc2-364x364.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc2-480x480.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc3.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc3-315x315.jpg" alt="View from the Staten Island Ferries to the Statue of Liberty" title="View from the Staten Island Ferries to the Statue of Liberty"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc3-364x364.jpg" alt="View from the Staten Island Ferries to the Statue of Liberty" title="View from the Staten Island Ferries to the Statue of Liberty"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc3-480x480.jpg" alt="View from the Staten Island Ferries to the Statue of Liberty" title="View from the Staten Island Ferries to the Statue of Liberty"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc4.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc4-315x315.jpg" alt="View from the Staten Island Ferries to the Statue of Liberty" title="View from the Staten Island Ferries to the Statue of Liberty"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc4-364x364.jpg" alt="View from the Staten Island Ferries to the Statue of Liberty" title="View from the Staten Island Ferries to the Statue of Liberty"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc4-480x480.jpg" alt="View from the Staten Island Ferries to the Statue of Liberty" title="View from the Staten Island Ferries to the Statue of Liberty"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc5.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc5-315x315.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc5-364x364.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc5-480x480.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc6.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc6-315x315.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc6-364x364.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc6-480x480.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc7.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc7-315x315.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc7-364x364.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc7-480x480.jpg" alt="View from the Staten Island Ferries to Downtown Manhattan" title="View from the Staten Island Ferries to Downtown Manhattan"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/nyc2009/nyc8.jpg"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/nyc2009/nyc8-315x315.jpg" alt="View from a Circle Line Harbor Cruise towards Ellis Island" title="View from a Circle Line Harbor Cruise towards Ellis Island"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/nyc2009/nyc8-364x364.jpg" alt="View from a Circle Line Harbor Cruise towards Ellis Island" title="View from a Circle Line Harbor Cruise towards Ellis Island"&gt;&lt;img class="small lazyload" data-src="/images/thumb/nyc2009/nyc8-480x480.jpg" alt="View from a Circle Line Harbor Cruise towards Ellis Island" title="View from a Circle Line Harbor Cruise towards Ellis Island"&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="figure align-right"&gt;
&lt;a class="reference external image-reference" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"&gt;&lt;img alt="Creative Commons License" src="/images/cc-by-nc-sa-3.0-88x31.png" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;These images are licensed under a &lt;a class="reference external" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"&gt;Creative Commons
Attribution-NonCommercial-ShareAlike 3.0 Unported License&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Misc"></category><category term="Holiday"></category></entry><entry><title>KDE 4.10 RC1 is out</title><link href="https://markusholtermann.eu/2012/12/kde-410-rc1-is-out/" rel="alternate"></link><published>2012-12-21T00:00:00+01:00</published><updated>2012-12-21T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-12-21:/2012/12/kde-410-rc1-is-out/</id><summary type="html">&lt;p class="first last"&gt;Two days ago the &lt;span class="caps"&gt;KDE&lt;/span&gt; team released the first release candidate of the
Plasma Workspaces towards version 4.10. The biggest changes that directly
catches ones eyes are the graphical modifications and improvements.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Two days ago the &lt;a class="reference external" href="http://kde.org/"&gt;&lt;span class="caps"&gt;KDE&lt;/span&gt;&lt;/a&gt; team &lt;a class="reference external" href="http://kde.org/announcements/announce-4.10-rc1.php"&gt;released&lt;/a&gt; the first release candidate of the
&lt;em&gt;Plasma Workspaces&lt;/em&gt; towards version 4.10. The biggest changes that directly
catches ones eyes are the graphical modifications and improvements. For me they
look much more seamless. I also like the new wallpaper. Looking under to hood,
there are many changes. I just want to point out my most favorite ones:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A new &lt;a class="reference external" href="http://qt.digia.com/Product/qt-quick/"&gt;QtQuick&lt;/a&gt; based screen locker.&lt;/li&gt;
&lt;li&gt;Scriptable effects – One can easily write desktop effects without knowledge of
C/C++&lt;/li&gt;
&lt;li&gt;The notification applet is now based on &lt;a class="reference external" href="http://en.wikipedia.org/wiki/QML"&gt;&lt;span class="caps"&gt;QML&lt;/span&gt;&lt;/a&gt; and feels much smoother&lt;/li&gt;
&lt;li&gt;The Nepomuk Indexer has been rewritten and is much faster as far as I can see.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course there are many, many more changes.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/kde410tb.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/kde410tb-315x315.jpg" alt="KDE 4.10 RC1" title="KDE 4.10 RC1"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/kde410tb-364x364.jpg" alt="KDE 4.10 RC1" title="KDE 4.10 RC1"&gt;&lt;img class="small lazyload" data-src="/images/thumb/kde410tb-480x480.jpg" alt="KDE 4.10 RC1" title="KDE 4.10 RC1"&gt;&lt;/a&gt;&lt;/div&gt;</content><category term="Linux"></category><category term="Arch Linux"></category><category term="KDE"></category></entry><entry><title>LVM on secondary LUKS device</title><link href="https://markusholtermann.eu/2012/11/lvm-on-secondary-luks-device/" rel="alternate"></link><published>2012-11-25T00:00:00+01:00</published><updated>2012-11-25T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-25:/2012/11/lvm-on-secondary-luks-device/</id><summary type="html">&lt;p class="first last"&gt;Today I ran into a problem when I set up my new &lt;span class="caps"&gt;SSD&lt;/span&gt;.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Today I ran into a problem when I set up my new &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Solid-state_drive"&gt;&lt;span class="caps"&gt;SSD&lt;/span&gt;&lt;/a&gt;. I have two fully
encrypted disks (one &lt;span class="caps"&gt;SSD&lt;/span&gt; and one &lt;span class="caps"&gt;HDD&lt;/span&gt;) and two &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Volume_group"&gt;volume groups (VGs)&lt;/a&gt; on top.
Each disk has its own &lt;span class="caps"&gt;VG&lt;/span&gt;. I thought about the following partition scheme.
&lt;tt class="docutils literal"&gt;sda&lt;/tt&gt; is the &lt;span class="caps"&gt;SSD&lt;/span&gt; and &lt;tt class="docutils literal"&gt;sdb&lt;/tt&gt; the &lt;span class="caps"&gt;HDD&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/dev/sda2            → LUKS → /dev/mapper/luksmain
/dev/sdb1            → LUKS → /dev/mapper/luksdata
/dev/mapper/luksmain → LVM  → /dev/mapper/main
/dev/mapper/luksdata → LVM  → /dev/mapper/data
&lt;/pre&gt;
&lt;p&gt;And from these block devices set up the following file systems:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/dev/mapper/main-root     /
/dev/sda1                 /boot
/dev/mapper/main-home     /home
/dev/mapper/data-var      /var
/dev/mapper/data-music    /home/Music
/dev/mapper/data-videos   /home/Videos
/dev/mapper/main-mysql    /var/lib/mysql
&lt;/pre&gt;
&lt;p&gt;As one can see, the &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Logical_volume"&gt;logical volume (&lt;span class="caps"&gt;LV&lt;/span&gt;)&lt;/a&gt; &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;data-var&lt;/span&gt;&lt;/tt&gt; is required by the system.
It contains e.g. the logs or the package cache.&lt;/p&gt;
&lt;p&gt;To use it during startup, enable the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lvm-on-luks&lt;/span&gt;&lt;/tt&gt; systemd service:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
systemctl enable lvm-on-luks.service
&lt;/pre&gt;
</content><category term="Linux"></category><category term="Arch Linux"></category><category term="Encryption"></category><category term="Linux"></category><category term="Security"></category></entry><entry><title>[CloudRAID] 6. Conclusion and Outlook</title><link href="https://markusholtermann.eu/2012/11/cloudraid-6-conclusion-and-outlook/" rel="alternate"></link><published>2012-11-24T00:00:00+01:00</published><updated>2012-11-24T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-24:/2012/11/cloudraid-6-conclusion-and-outlook/</id><summary type="html">&lt;p class="first last"&gt;This post is the last post of the blog series about the student
research paper CloudRAID. It summarizes the pros and cons and provides some
prospects that would improve CloudRAID.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is the last post of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is the last part of the student research paper about &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;. Florian
&lt;a class="reference external" href="http://blog.fbausch.de/2012/11/24/cloudraid-6-conclusion-and-outlook/"&gt;summarizes the pros and cons&lt;/a&gt; of our implementation. Furthermore, he shows some
ideas that could make &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; more convenient, e.g. adding a search
possibility.&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 5. Benchmarks</title><link href="https://markusholtermann.eu/2012/11/cloudraid-5-benchmarks/" rel="alternate"></link><published>2012-11-21T00:00:00+01:00</published><updated>2012-11-21T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-21:/2012/11/cloudraid-5-benchmarks/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It provides some timing insights that on our
prototype and final implementation.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;5. Benchmarks&lt;/h2&gt;
&lt;p&gt;Since the performance is an integral part and a key aspect of the software and
even more one of the most important piece, the following chapter will show some
performance tests and benchmark results.&lt;/p&gt;
&lt;h3&gt;5.1. Environments&lt;/h3&gt;
&lt;p&gt;In order to make the benchmarks reproducible this chapter will set up some
benchmark environments. They define the hardware as well as the software that is
used for the benchmark tests.&lt;/p&gt;
&lt;h4&gt;Environment 1&lt;/h4&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="25%" /&gt;
&lt;col width="75%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;CPU&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Intel® Core™ 2 Duo &lt;span class="caps"&gt;CPU&lt;/span&gt; P8600 &amp;#64; 2.40 GHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;4 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Hard disk&lt;/td&gt;
&lt;td&gt;Samsung &lt;span class="caps"&gt;HM320II&lt;/span&gt;, 16 MiB Cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File system&lt;/td&gt;
&lt;td&gt;ext4, Block size: 4096 Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk Encryption&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;, cipher: aes-xts-plain, 512 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk Management&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;: 2.02.88(2), Library: 1.02.67 Driver: 4.22.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Operating System&lt;/td&gt;
&lt;td&gt;Arch Linux 64bit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;Environment 2&lt;/h4&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="25%" /&gt;
&lt;col width="75%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;CPU&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Intel® Core™ 2 Duo &lt;span class="caps"&gt;CPU&lt;/span&gt; E6600 &amp;#64; 2.40 GHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;2 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Hard disk&lt;/td&gt;
&lt;td&gt;2x Samsung SpinPoint &lt;span class="caps"&gt;HD501LJ&lt;/span&gt;, 16 MiB Cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File system&lt;/td&gt;
&lt;td&gt;ext4, Block size: 4096 Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk Encryption&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;, cipher: aes-xts-plain, 512 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Disk Management&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;: 2.02.88(2), Library: 1.02.67 Driver: 4.22.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Software &lt;span class="caps"&gt;RAID&lt;/span&gt; 1, mdadm 3.2.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Operating System&lt;/td&gt;
&lt;td&gt;Arch Linux 64bit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;Environment 3&lt;/h4&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="25%" /&gt;
&lt;col width="75%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;CPU&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;AMD&lt;/span&gt; Phenom™ &lt;span class="caps"&gt;II&lt;/span&gt; X6 1090T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;16 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Hard disk&lt;/td&gt;
&lt;td&gt;Seagate &lt;span class="caps"&gt;ST31000524AS&lt;/span&gt;, 32 MiB Cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File system&lt;/td&gt;
&lt;td&gt;ext4, Block size: 4096 Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Operating System&lt;/td&gt;
&lt;td&gt;Arch Linux 64bit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;Environment 4&lt;/h4&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="25%" /&gt;
&lt;col width="75%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;CPU&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Intel® Core™ i7 920 &amp;#64; 2.67 GHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;8 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Hard disk&lt;/td&gt;
&lt;td&gt;Kingston &lt;span class="caps"&gt;SH100S3&lt;/span&gt; 120G (&lt;span class="caps"&gt;SSD&lt;/span&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File system&lt;/td&gt;
&lt;td&gt;ext4, Block size: 4096 Byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Operating System&lt;/td&gt;
&lt;td&gt;Arch Linux 64bit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;5.2. Comparison of Java, Python, and C&lt;/h3&gt;
&lt;p&gt;One of the key functions of &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; is to ensure data-security and
data-safety. Therefore &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; uses the previously explained &lt;span class="caps"&gt;ARC4&lt;/span&gt;
encryption standard andthe &lt;span class="caps"&gt;RAID&lt;/span&gt; level 5 implementation. Since this time critical
operation is used during each &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; cloud interaction, a run-time
evaluation during the prototype state was interesting.&lt;/p&gt;
&lt;p&gt;To make this key component fast, there have been prototypes in Java, Python 2
and C. All three implementations were working with the same algorithm but of
course had a big differences when comparing their run-time.&lt;/p&gt;
&lt;p&gt;The following table shows the run-time for the &amp;#8220;split&amp;#8221; process on a 15 MiB file
using bitwise &lt;span class="caps"&gt;RAID&lt;/span&gt; level 5 without encryption and without hash calculation.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="52%" /&gt;
&lt;col width="48%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Language&lt;/th&gt;
&lt;th class="head"&gt;Runtime[sec]&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;C (Clang)&lt;/td&gt;
&lt;td&gt;1.526&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;C (Clang -O3)&lt;/td&gt;
&lt;td&gt;1.123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;C (&lt;span class="caps"&gt;GCC&lt;/span&gt;)&lt;/td&gt;
&lt;td&gt;1.975&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;C (&lt;span class="caps"&gt;GCC&lt;/span&gt; -O3)&lt;/td&gt;
&lt;td&gt;1.490&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;155.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Python2.6&lt;/td&gt;
&lt;td&gt;122.300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Python2.7&lt;/td&gt;
&lt;td&gt;121.909&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;PyPy 1.6&lt;/td&gt;
&lt;td&gt;20.967&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Table 12: Prototype benchmarks for Java, Python and C&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As one can see from the previous table the run-time for Java is more than 2.5
times as long as for CPython2, more than 7 times longer than for &lt;a class="reference external" href="http://pypy.org/"&gt;PyPy&lt;/a&gt; and 77
times longer than the native C implementation. The speed of CPython2 and PyPy is
most likely due to the small overhead between the Python interpreter and the
underlying native code, while the Java implementation must leave the Java
Runtime Environment (&lt;span class="caps"&gt;JRE&lt;/span&gt;) for each read and write operation, since they are also
natively implemented and a &lt;span class="caps"&gt;JNI&lt;/span&gt; call is relatively expensive. The speed advantage
of the PyPy based execution is probably based on the just-in-time compiler that
can increase the speed many times.&lt;/p&gt;
&lt;p&gt;The results from this prototype gave the crucial factor to implement the &lt;span class="caps"&gt;RAID&lt;/span&gt;
feature in native C – independent of the programming language that would be used
for the remaining part of the software. To get better benchmarking results for C
there was a test with a 1.9 &lt;span class="caps"&gt;GB&lt;/span&gt; file including compiler optimization like the -O3
flag:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="52%" /&gt;
&lt;col width="48%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Language&lt;/th&gt;
&lt;th class="head"&gt;Runtime[sec]&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;C (Clang -O3)&lt;/td&gt;
&lt;td&gt;1.123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;C (&lt;span class="caps"&gt;GCC&lt;/span&gt; -O3)&lt;/td&gt;
&lt;td&gt;1.490&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Table 13: Large file benchmarks for &lt;span class="caps"&gt;GCC&lt;/span&gt; and Clang&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This table clearly shows, that &lt;a class="reference external" href="http://clang.llvm.org/"&gt;Clang&lt;/a&gt; is doing a better optimization than &lt;span class="caps"&gt;GCC&lt;/span&gt;
does. But nevertheless, using the &lt;span class="caps"&gt;JNI&lt;/span&gt;, the performance of both implementations
changed and &lt;span class="caps"&gt;GCC&lt;/span&gt; was slightly faster than Clang.&lt;/p&gt;
&lt;h3&gt;5.3. &lt;span class="caps"&gt;RAID&lt;/span&gt; Level 5 Benchmarks&lt;/h3&gt;
&lt;p&gt;To get the performance of the final implementation, some benchmarks on the
environments, that have been defined in 6.1 on page 54, have been performed.
Therefore files of the sizes 10 kib, 100 kiB, 1 MiB, 20 MiB, 50 MiB, 100 MiB,
250 MiB, 500 MiB and 1 GiB have been created, split and merged. Every complete
process (creation - split - merge) has been run eight times for each size on
each benchmark environment. On the third environment additional tests for the
sizes 2 GiB, 3 GiB, 4 GiB and 5 GiB have been performed to find and show
anomalies for large files.&lt;/p&gt;
&lt;h4&gt;5.3.1. Split&lt;/h4&gt;
&lt;p&gt;Figure 1 shows the average run-time for the split process in the given benchmark
environments. One can see that for the files 100 kiB to 100 MiB the run-time
increases mostly linear to the file size for every environment. For the range
from 10 kiB to 100 kiB the run-time even slightly decreases, relative to the
file size, probably caused by the overhead opening the five files.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_combined_raid5_split.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_combined_raid5_split-315x315.jpg" alt="Runtime for split in various benchmark environments" title="Runtime for split in various benchmark environments"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_combined_raid5_split-364x364.jpg" alt="Runtime for split in various benchmark environments" title="Runtime for split in various benchmark environments"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_combined_raid5_split-480x480.jpg" alt="Runtime for split in various benchmark environments" title="Runtime for split in various benchmark environments"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Figure 1: Runtime for split in various benchmark environments&lt;/p&gt;
&lt;p&gt;Using the T400 notebook with above specifications, the run-time increases for
the files larger than 100 MiB. There is no proved explanation for this
phenomenon yet, but it has been appeared during all benchmark runs and is not
just a single outlier distorting the statistics.&lt;/p&gt;
&lt;p&gt;Environment 2 keeps a linear run-time up to files of size 500 MiB and is much
slower for files of size 1 GiB.&lt;/p&gt;
&lt;p&gt;For the third and forth environments the run-time is linear for all files, even
for the huge files. As one can see in figure 2, the relative run-time increases
for files of size 2 GiB and larger as well.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_env3_raid5_split_zoom.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_split_zoom-315x315.jpg" alt="Split statistics for Tower 2 for huge files" title="Split statistics for Tower 2 for huge files"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_split_zoom-364x364.jpg" alt="Split statistics for Tower 2 for huge files" title="Split statistics for Tower 2 for huge files"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_split_zoom-480x480.jpg" alt="Split statistics for Tower 2 for huge files" title="Split statistics for Tower 2 for huge files"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Figure 2: Split statistics for Tower 2 for huge files&lt;/p&gt;
&lt;h4&gt;5.3.2. Merge&lt;/h4&gt;
&lt;p&gt;After a file has been split into its three &lt;em&gt;device files&lt;/em&gt; and regarding
information has been stored in the &lt;em&gt;meta data file&lt;/em&gt;, the benchmark tool combines
these &lt;em&gt;device files&lt;/em&gt; to the original file. Again one can see from figure 3 that
the overall process is mostly linear, except for the range of very small and
very large files. Similar to the split process the hard drive accesses to open
the files is probably the main reason for the decreasing relative run-time
between 10 kiB and 100 kiB.&lt;/p&gt;
&lt;p&gt;Similar to the split process, environment 3 shows an increasing relative
run-time for files greater or equal to 2 GiB as one can determine from figure 4
on page 60.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_combined_raid5_merge.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_combined_raid5_merge-315x315.jpg" alt="Runtime for merge in various benchmark environments" title="Runtime for merge in various benchmark environments"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_combined_raid5_merge-364x364.jpg" alt="Runtime for merge in various benchmark environments" title="Runtime for merge in various benchmark environments"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_combined_raid5_merge-480x480.jpg" alt="Runtime for merge in various benchmark environments" title="Runtime for merge in various benchmark environments"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Figure 3: Runtime for merge in various benchmark environments&lt;/p&gt;
&lt;p&gt;Further comparisons of the benchmark environments show differences in the hard
drive caches. While the hard drives in the environments 1 and 2 have a cache
size of 16 MiB the hard disks from environment 3 provide 32 MiB. Comparing the
ages of the used hard disks lead to similar assumptions. While the hard disks
for the first and the second environment are about three, respectively five,
years old, the age of hard disks used in environments 3 and 4 are about one
year.&lt;/p&gt;
&lt;p&gt;Besides that, the storage devices for the primary two environments are encrypted
while the other two hard drives are plain. But due to the implementation of the
underlying encryption as part of the &lt;em&gt;Device Mapper&lt;/em&gt; of the Linux Kernel, the
hardware access is fully transparent and does not do any further hits or
accesses on caches. Thus this has probably no or at least not a high impact on
the hard drive throughput and only affects the &lt;em&gt;Central Processing Unit&lt;/em&gt; (&lt;span class="caps"&gt;CPU&lt;/span&gt;)
usage.&lt;/p&gt;
&lt;blockquote&gt;
[The] Device-mapper uses block devices, such as hard disks and flash-storage
devices, and represents them as other block devices by adding various
features. The &lt;span class="caps"&gt;LVM&lt;/span&gt; for example provides flexible partitioning of the block
device, while dm-crypt adds a transparent symmetric encryption layer to the
device by using the Linux in-kernel Crypto &lt;span class="caps"&gt;API&lt;/span&gt;. &lt;a class="citation-reference" href="#hol12" id="id1"&gt;[Hol12]&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Finally, the slight difference in the throughput between the &lt;span class="caps"&gt;SSD&lt;/span&gt; and the third
environment is probably caused by the amount of available memory. While the
former benchmark environment can use up to 8 &lt;span class="caps"&gt;GB&lt;/span&gt; minus the memory used by the &lt;span class="caps"&gt;OS&lt;/span&gt;
itself, the latter has twice that memory available (minus those used by the &lt;span class="caps"&gt;OS&lt;/span&gt;).
Thus the files are being cached in in-memory and are not even written to disk.
But unfortunately there is no way to check whether the data has been written to
disk. Another influence leading to the performance disadvantages of in the forth
environment might be caused by the &lt;span class="caps"&gt;SATA&lt;/span&gt; controller.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_env3_raid5_merge_zoom.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_merge_zoom-315x315.jpg" alt="Merge statistics for third environment for huge files" title="Merge statistics for third environment for huge files"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_merge_zoom-364x364.jpg" alt="Merge statistics for third environment for huge files" title="Merge statistics for third environment for huge files"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_merge_zoom-480x480.jpg" alt="Merge statistics for third environment for huge files" title="Merge statistics for third environment for huge files"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Figure 4: Merge statistics for third environment for huge files&lt;/p&gt;
&lt;h4&gt;5.3.3. Comparison&lt;/h4&gt;
&lt;p&gt;While the figures above only show the compared speed for either split or merge
for all four benchmark environments, figure 5 illustrates the relative speed
between the split and merge processes for the benchmark environments. One can
see, that the split process takes much more time than the merge process for the
same file size. This is most likely caused by the number of bytes that are
written to the hard disk. While a call to &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; with an input file of
size &lt;tt class="docutils literal"&gt;n&lt;/tt&gt; writes &lt;tt class="docutils literal"&gt;1.5 × n + 518 bytes&lt;/tt&gt; and reads &lt;tt class="docutils literal"&gt;n&lt;/tt&gt; bytes, the regarding
merge done by &lt;tt class="docutils literal"&gt;merge_file()&lt;/tt&gt; only writes n bytes but reads &lt;tt class="docutils literal"&gt;1.5 × n + 518
bytes&lt;/tt&gt;. Since reading is generally much faster than writing this explains the
speed of merge processes.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_env1_raid5_comparison.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_env1_raid5_comparison-315x315.jpg" alt="Split and merge comparisons for environment 1" title="Split and merge comparisons for environment 1"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_env1_raid5_comparison-364x364.jpg" alt="Split and merge comparisons for environment 1" title="Split and merge comparisons for environment 1"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_env1_raid5_comparison-480x480.jpg" alt="Split and merge comparisons for environment 1" title="Split and merge comparisons for environment 1"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_env2_raid5_comparison.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_env2_raid5_comparison-315x315.jpg" alt="Split and merge comparisons for environment 2" title="Split and merge comparisons for environment 2"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_env2_raid5_comparison-364x364.jpg" alt="Split and merge comparisons for environment 2" title="Split and merge comparisons for environment 2"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_env2_raid5_comparison-480x480.jpg" alt="Split and merge comparisons for environment 2" title="Split and merge comparisons for environment 2"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_env3_raid5_comparison.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_comparison-315x315.jpg" alt="Split and merge comparisons for environment 3" title="Split and merge comparisons for environment 3"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_comparison-364x364.jpg" alt="Split and merge comparisons for environment 3" title="Split and merge comparisons for environment 3"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_env3_raid5_comparison-480x480.jpg" alt="Split and merge comparisons for environment 3" title="Split and merge comparisons for environment 3"&gt;&lt;/a&gt;&lt;a class="col s12 m6 l4" href="/images/cloudraid/final_env4_raid5_comparison.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/cloudraid/final_env4_raid5_comparison-315x315.jpg" alt="Split and merge comparisons for environment 4" title="Split and merge comparisons for environment 4"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/cloudraid/final_env4_raid5_comparison-364x364.jpg" alt="Split and merge comparisons for environment 4" title="Split and merge comparisons for environment 4"&gt;&lt;img class="small lazyload" data-src="/images/thumb/cloudraid/final_env4_raid5_comparison-480x480.jpg" alt="Split and merge comparisons for environment 4" title="Split and merge comparisons for environment 4"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Figure 5a - 5d: Split and merge comparisons for environments one to four&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;table class="docutils citation" frame="void" id="hol12" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[Hol12]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Markus Holtermann. Bachelorthesis: Testing Approach for an in-kernel Crypto Functionality – For Linux on System z, June 4, 2012. &lt;em&gt;(unpublished)&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="C/C++"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 4. Implementation (Continuation 2)</title><link href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation-continuation-2/" rel="alternate"></link><published>2012-11-18T00:00:00+01:00</published><updated>2012-11-18T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-18:/2012/11/cloudraid-4-implementation-continuation-2/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It features the compression &lt;span class="caps"&gt;API&lt;/span&gt; and client
implementation.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After the implementation of the &lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation/"&gt;CloudRAID backend has been introduced&lt;/a&gt; and the
&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation-continuation/"&gt;specification of the &lt;span class="caps"&gt;REST&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; endpoints&lt;/a&gt; is published, Florian is describing
the &lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-4-implementation-continuation/"&gt;compression &lt;span class="caps"&gt;API&lt;/span&gt; and the client application&lt;/a&gt; on his blog in a new post.&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 4. Implementation (Continuation)</title><link href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation-continuation/" rel="alternate"></link><published>2012-11-15T00:00:00+01:00</published><updated>2012-11-15T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-15:/2012/11/cloudraid-4-implementation-continuation/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It features the &lt;span class="caps"&gt;API&lt;/span&gt; implementation.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;4. Implementation&lt;/h2&gt;
&lt;h3&gt;4.2. RESTful &lt;span class="caps"&gt;API&lt;/span&gt; Endpoint Specifications&lt;/h3&gt;
&lt;h4&gt;4.2.1. &lt;span class="caps"&gt;URL&lt;/span&gt; Mapping for &lt;span class="caps"&gt;API&lt;/span&gt; Endpoints&lt;/h4&gt;
&lt;p&gt;The default &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; package contains a RESTful &lt;span class="caps"&gt;HTTP&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt;. This is realized
using &lt;em&gt;Java Servlets&lt;/em&gt;. A Java web and application server will be used to deploy
the servlet. To activate the servlet this is bound to a base path that will be
required to be prefixed to the &lt;em&gt;Uniform Resource Locator&lt;/em&gt; (&lt;span class="caps"&gt;URL&lt;/span&gt;) path. The
actual functionality provided by this servlet will then be bound to sub paths
as described in chapters 4.2.2 to 4.2.4 (see below). The basic concept of how
the mapping between the sub paths and &lt;span class="caps"&gt;URL&lt;/span&gt; parameters is done, has been taken
from the &lt;a class="reference external" href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.4/topics/http/urls/"&gt;&lt;span class="caps"&gt;URL&lt;/span&gt; dispatcher&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The complete pattern and mapping implementation is done in the class
&lt;tt class="docutils literal"&gt;RestApiUrlMapping&lt;/tt&gt; within the package
&lt;tt class="docutils literal"&gt;de.dhbw_mannheim.cloudraid.api.impl&lt;/tt&gt; and is part of the &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;-&lt;span class="caps"&gt;REST&lt;/span&gt;
bundle. The &lt;tt class="docutils literal"&gt;RestApiUrlMapping&lt;/tt&gt; constructor takes two to four parameter of
various type as one can see in listing 9 below:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The first parameter must be the pattern that needs to be matched. This can
either be a &lt;tt class="docutils literal"&gt;java.lang.String&lt;/tt&gt; or a regular expression pattern of type
&lt;tt class="docutils literal"&gt;java.util.regex.Pattern&lt;/tt&gt;. If a &lt;tt class="docutils literal"&gt;String&lt;/tt&gt; is passed, this is converted to
a &lt;tt class="docutils literal"&gt;Pattern&lt;/tt&gt; and hence must be a correct regular expression. Otherwise a
&lt;tt class="docutils literal"&gt;IllegalArgumentException&lt;/tt&gt; will be thrown.&lt;/li&gt;
&lt;li&gt;The second, optional, parameter can be used to restrict a mapping to a
specific &lt;span class="caps"&gt;HTTP&lt;/span&gt; request method. This allows a developer to have different
callback functions for the same &lt;span class="caps"&gt;URL&lt;/span&gt; path, depending on the &lt;span class="caps"&gt;HTTP&lt;/span&gt; method.&lt;/li&gt;
&lt;li&gt;The remaining parameters define the callback function which is called if the
&lt;span class="caps"&gt;URL&lt;/span&gt; matches a &lt;span class="caps"&gt;HTTP&lt;/span&gt; request. This can either be a single parameter of type
&lt;tt class="docutils literal"&gt;java.lang.reflect.Method&lt;/tt&gt; or two parameters whereof the first must be of
type &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Class&amp;lt;?&amp;gt;&lt;/span&gt;&lt;/tt&gt; and the second of type &lt;tt class="docutils literal"&gt;java.lang.String&lt;/tt&gt;. In the latter
case, the static function &lt;tt class="docutils literal"&gt;findFunction()&lt;/tt&gt; will be used to resolve both
parameters to a &lt;tt class="docutils literal"&gt;Method&lt;/tt&gt;. If the (resolved) method is null, this will
result in a &lt;tt class="docutils literal"&gt;IllegalArgumentException&lt;/tt&gt;. If the method needs to be
resolved, any failure will raise as &lt;tt class="docutils literal"&gt;NoSuchMethodException&lt;/tt&gt; or
&lt;tt class="docutils literal"&gt;SecurityException&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;de.dhbw_mannheim.cloudraid.api.impl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RestApiUrlMapping&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="nf"&gt;findFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 9: The &lt;span class="caps"&gt;URL&lt;/span&gt; mapping &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The actual mapping will be initialized in the constructor of the
&lt;tt class="docutils literal"&gt;RestApiServlet&lt;/tt&gt; as shown in the listing 10. The shipped &lt;span class="caps"&gt;REST&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; uses only
the &lt;tt class="docutils literal"&gt;RestApiUrlMapping&lt;/tt&gt; constructor defined in lines 17 and 18 of listing 9
because of its simplicity. But one can use this class to extend the &lt;span class="caps"&gt;API&lt;/span&gt; with
own functions. Hence the other constructors are still in the code and will not
be removed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;de.dhbw_mannheim.cloudraid.api.impl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RestApiServlet&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;HttpServlet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RestApiServlet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;RestApiServlet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;^/api/info/$&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RestApiServlet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;apiInfo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;RestApiServlet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RestApiUrlMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;^/file/([^/]+)/$&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;DELETE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RestApiServlet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;&amp;quot;fileDelete&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// Similar for other URL sub paths&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 10: The &lt;span class="caps"&gt;URL&lt;/span&gt; mapping initialization&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On each request to the &lt;tt class="docutils literal"&gt;RestApiServlet&lt;/tt&gt; the list of available mappings will
be checked for a matching mapping with the path information of the current
request and its &lt;span class="caps"&gt;HTTP&lt;/span&gt; method. If a valid mapping exists, the regarding callback
function will be executed. This callback function must accept exactly three
arguments:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;javax.servlet.http.HttpServletRequest req&lt;/tt&gt;: The current request. This
variable will contain session information, the &lt;span class="caps"&gt;HTTP&lt;/span&gt; headers and allows to
access the request body.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;de.dhbw_mannheim.cloudraid.api.responses.IRestApiResponse resp&lt;/tt&gt;: The
response object that will be returned to the client. It will contain a &lt;span class="caps"&gt;HTTP&lt;/span&gt;
response code as defined by the specifications in the following chapters and
a payload, depending on the request.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;java.util.ArrayList&amp;lt;String&amp;gt; args&lt;/tt&gt;: The &lt;span class="caps"&gt;URL&lt;/span&gt; patterns may contain matching
groups, enclosed by &lt;tt class="docutils literal"&gt;(&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;)&lt;/tt&gt;. The content of each matching group will
be added to this parameter and is therefore available to the callback
function. The &lt;em&gt;Create, Read, Update, Delete&lt;/em&gt; (&lt;span class="caps"&gt;CRUD&lt;/span&gt;) endpoints as defined in
chapter &lt;em&gt;4.2.3 – File Related Function&lt;/em&gt; make use of a single matching group
that contains the file name of the uploaded file.[/olist]&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;4.2.2. Version Information&lt;/h4&gt;
&lt;p&gt;The RESTful &lt;span class="caps"&gt;API&lt;/span&gt; already supports many features. But since the &lt;span class="caps"&gt;API&lt;/span&gt; might change
someday, some kind of versioning is needed to tell the client side, which
features are supported. This information, along with the version information of
the &lt;em&gt;Core-&lt;/em&gt;, &lt;em&gt;Metadata-&lt;/em&gt; and &lt;em&gt;Configuration-Services&lt;/em&gt; and the currently running
&lt;span class="caps"&gt;RAID&lt;/span&gt; version and the versions of the activated storage connectors is provided
via this &lt;span class="caps"&gt;URL&lt;/span&gt;.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;/api/info/&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Displays the version and author/vendor information about
various service components, such as the Core service, the
cloud storage connectors, the configuration, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Usage:&lt;/td&gt;
&lt;td&gt;Used on client site to verify that the client is able to
handle the provided &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – Success – No special meaning in this response&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: &lt;span class="caps"&gt;API&lt;/span&gt; and service information&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;HTTP/1.1 200 OK
X-Powered-By: CloudRAID/0.2
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Server: Jetty(6.1.x)

Core-Service:CloudRAID-Core v0.0.1.prealpha by cloudraid
RAID-Version:CloudRAID-RAID5 v0.0.2prealpha by cloudraid
Metadata-Service:CloudRAID-Metadata v0.0.1.prealpha by cloudraid
Configuration-Service:CloudRAID-Config v0.0.1.prealpha by cloudraid
API-Service:CloudRAID-RESTful v0.0.1.prealpha by cloudraid
Storage-Connector-0:CloudRAID-Dropbox v0.0.1.prealpha by cloudraid
Storage-Connector-1:CloudRAID-AmazonS3 v0.0.1.prealpha by cloudraid
Storage-Connector-2:CloudRAID-UbuntuOne v0.0.1.prealpha by cloudraid
API-Version:0.2
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 11: Example &lt;span class="caps"&gt;HTTP&lt;/span&gt; response: header elements and body&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;4.2.3. File Related Function&lt;/h4&gt;
&lt;p&gt;All &lt;span class="caps"&gt;CRUD&lt;/span&gt; functions are provided by the following four URLs. The endpoint for
all of them is &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/file/([^/]+)/&lt;/span&gt;&lt;/tt&gt;. Based on the &lt;span class="caps"&gt;HTTP&lt;/span&gt; method, a file is either
being created, downloaded, removed or updated.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Create&lt;/em&gt; and &lt;em&gt;Update&lt;/em&gt; endpoints have to handle a huge amount of data.
Therefore they use the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;PUT&lt;/span&gt;&lt;/tt&gt; methods respectively. In both cases
the file will be trans- mitted synchronously to the &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; service. All
ongoing steps are going to be asynchronous. They do not influence the &lt;span class="caps"&gt;HTTP&lt;/span&gt;
response to the upload.&lt;/p&gt;
&lt;p&gt;On the other hand, the &lt;em&gt;Delete&lt;/em&gt; and &lt;em&gt;Download&lt;/em&gt; functions are completely
synchronous.  They use the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;DELETE&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/tt&gt; &lt;span class="caps"&gt;HTTP&lt;/span&gt; methods. The cloud
storage connectors will trigger the deletion or get request to the cloud
storage providers and are blocking up to their response or until the regarding
network protocol throws a timeout error. From that follows, that a failure to
the &amp;#8220;outbound&amp;#8221; connections from the &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; service may block a client
application, or at least the thread the connection runs in, for some time.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;DELETE&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/file/([^/]+)/&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Calls on this endpoint will trigger a synchronous deletion of
the given file from the cloud storage connectors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – File has been deleted or does not even exist&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;404 – File cannot be found in the meta data manager&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;500 – An error occurred during deleting the file&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: file deletion&lt;/em&gt;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/file/([^/]+)/&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Calls on this endpoint will trigger a synchronous download of
the requested file from the cloud storage, a merge to the
original file and download to the client.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – File has been downloaded&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;404 – File cannot be found in the meta data manager&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;500 – An error occurred during downloading the file&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: file download&lt;/em&gt;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/file/([^/]+)/&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Calls on this endpoint will trigger a synchronous upload of
the &lt;em&gt;new&lt;/em&gt; file. A successful upload of the file will split and
push it to the cloud storage providers asynchronously.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;201 – File has been uploaded&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;409 – File already exists&lt;/li&gt;
&lt;li&gt;411 – No content length specified&lt;/li&gt;
&lt;li&gt;500 – An error occurred while storing the new file&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: file upload of a new file&lt;/em&gt;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;PUT&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/file/([^/]+)/&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Calls on this endpoint will trigger a synchronous upload of
the &lt;em&gt;existing&lt;/em&gt; file. A successful upload of the file will
split and push it to the cloud storage providers
asynchronously.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;201 – File has been uploaded&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;404 – File cannot be found&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;411 – No content length specified&lt;/li&gt;
&lt;li&gt;500 – An error occurred while storing the new file&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: file upload of an existing file&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;While using a client application, the user needs to know, which files exist and
can be downloaded and, if the system is under heavy usage, what the states of
the uploads are. The following &lt;span class="caps"&gt;URL&lt;/span&gt; shows all files a user has access to and
displays those information.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;/list/&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Calls on this endpoint will return a list of all files the
current user has access to.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – Success – No special meaning in this response&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;500 – An error occurred while storing the new file&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: list all accessible file&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;4.2.4. User Related Functions&lt;/h4&gt;
&lt;p&gt;All connections, except the &lt;span class="caps"&gt;API&lt;/span&gt; version request, need some kind of
authorization or provide and initiate it. The authentication is done on user
base. The creation, authentication and de-authentication (logout) is performed
on the &lt;em&gt;/user/&lt;/em&gt; endpoint namespace. Besides that, a user is able to change the
login password, while user deletion requests are not allowed. This is basically
due to the fact, that a deletion should trigger a deletion of all files
belonging to the user from the cloud storage providers. But since this step is
non-reversible and a full data loss will be the consequence, the decision to
&lt;em&gt;not&lt;/em&gt; implement this function has been made.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;/user/add/&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;Calling this endpoint will add a new user to the system.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;X-Username: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;USERNAME&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;X-Password: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;PASSWORD&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;X-Confirm: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CONFIRMATION&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – Success – No special meaning in this response&lt;/li&gt;
&lt;li&gt;400 – The user name is invalid or missing or the password
and its confirmation are missing or do not match&lt;/li&gt;
&lt;li&gt;406 – The user is already logged in&lt;/li&gt;
&lt;li&gt;500 – An error occurred while retrieving the list&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: add new user&lt;/em&gt;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;/user/auth/&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;This endpoint will be used to authenticate the requesting
user.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;X-Username: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;USERNAME&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;X-Password: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;PASSWORD&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;202 – Success – User has been authenticated&lt;/li&gt;
&lt;li&gt;400 – The user name is invalid or missing or the password is
missing or invalid&lt;/li&gt;
&lt;li&gt;406 – The user is already logged in&lt;/li&gt;
&lt;li&gt;503 – The session could not be created&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: authenticate a user&lt;/em&gt;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;/user/chgpw/&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;This endpoint must be used to change a user&amp;#8217;s password.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;X-Username: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;USERNAME&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;X-Password: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;PASSWORD&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;X-Confirm: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CONFIRMATION&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – Success – Password has been changed&lt;/li&gt;
&lt;li&gt;400 – The user name is invalid or missing or the password
and its confirmation are missing or do not match&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;500 – An error occurred while updating the user record&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: change a user password&lt;/em&gt;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="18%" /&gt;
&lt;col width="82%" /&gt;
&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Method:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Endpoint:&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;/user/auth/logout/&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Description:&lt;/td&gt;
&lt;td&gt;This endpoint must be used to log-off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parameter:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;Cookie: &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;JSESSIONID&lt;/span&gt;=&lt;span class="caps"&gt;VALUE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;HTTP&lt;/span&gt; Response:&lt;/td&gt;
&lt;td&gt;&lt;ul class="first last simple"&gt;
&lt;li&gt;200 – Success – User has been logged out and session has
been invalidated&lt;/li&gt;
&lt;li&gt;401 – The requesting user is not logged in&lt;/li&gt;
&lt;li&gt;405 – The session identifier has not been transferred via
cookie&lt;/li&gt;
&lt;li&gt;503 – The session given by the identifier does not exist&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt;: user logout&lt;/em&gt;&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 4. Implementation</title><link href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation/" rel="alternate"></link><published>2012-11-13T00:00:00+01:00</published><updated>2012-11-13T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-13:/2012/11/cloudraid-4-implementation/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It features the underlying &lt;span class="caps"&gt;JNI&lt;/span&gt; implementation.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After Florian&amp;#8217;s &lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept-continuation/"&gt;publications about the architecture&lt;/a&gt; the concrete
implementation design will follow.&lt;/p&gt;
&lt;h2&gt;4. Implementation&lt;/h2&gt;
&lt;h3&gt;4.1. &lt;span class="caps"&gt;JNI&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The supplied &lt;span class="caps"&gt;RAID&lt;/span&gt; 5 implementation uses &lt;span class="caps"&gt;SHA&lt;/span&gt;-256 for integrity and validity
checks. The hash implementation has been taken from the &lt;a class="reference external" href="http://apr.apache.org/"&gt;Apache Portable
Runtime Project (&lt;span class="caps"&gt;APR&lt;/span&gt;)&lt;/a&gt; that has been published by the &lt;a class="reference external" href="http://apache.org/"&gt;Apache Software
Foundation&lt;/a&gt; under the terms of the &lt;a class="reference external" href="http://www.apache.org/licenses/LICENSE-2.0.html"&gt;Apache License version 2.0&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;4.1.1. Pre-compiler Flags and Constants&lt;/h4&gt;
&lt;p&gt;The native &lt;span class="caps"&gt;RAID&lt;/span&gt; 5 level implementation uses multiple pre-compiler flags and
constants to control the split and merge processes:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;&lt;/tt&gt; specifies how many bytes the library will write at once
to each device file (default 1024 bytes). Hence &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt; × 2&lt;/tt&gt; bytes
are read at once from the input file.&lt;/li&gt;
&lt;li&gt;Normally all data will be encrypted before it is being split into the device
files. This can be changed by setting &lt;tt class="docutils literal"&gt;ENCRYPT_DATA&lt;/tt&gt; to 0.&lt;/li&gt;
&lt;li&gt;The constant &lt;tt class="docutils literal"&gt;ENCRYPTION_SALT_BYTES&lt;/tt&gt; specifies the size of the salt in
bytes. The default value is 256.&lt;/li&gt;
&lt;li&gt;For debugging purposes, the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;DEBUG&lt;/span&gt;&lt;/tt&gt; constant can be set. There are three
debug levels possible, where 1 prints minimum debug information and 3 all. A
value of 0 disables entirely disables the debug output.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;_NAME_&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;_VENDOR_&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;_VERSION_&lt;/tt&gt; contain meta data for this
library as exemplarily shown in listing 1. They information will be used by
the native functions &lt;tt class="docutils literal"&gt;getName()&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;getVendor()&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;getVersion()&lt;/tt&gt; as
explained in chapter &lt;a class="reference external" href="#function-signatures"&gt;4.1.3 - Function Signatures&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#define RAID5BLOCKSIZE 1024&lt;/span&gt;
&lt;span class="cp"&gt;#define ENCRYPT_DATA 1&lt;/span&gt;
&lt;span class="cp"&gt;#define ENCRYPTION_SALT_BYTES 256&lt;/span&gt;
&lt;span class="cp"&gt;#define DEBUG 0&lt;/span&gt;
&lt;span class="cp"&gt;#define _NAME_ &amp;quot;CloudRAID-RAID5&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;#define _VENDOR_ &amp;quot;CloudRAID Team&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;#define _VERSION_ &amp;quot;1.0.0-beta.1&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 1: Pre-compiler flags and constants&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;4.1.2. Structs&lt;/h4&gt;
&lt;p&gt;For internal storage purposes, the native &lt;span class="caps"&gt;RAID&lt;/span&gt; implementation needs two C
structs that are shown in listing 2:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Lines 1 to 6 show the &lt;span class="caps"&gt;SHA&lt;/span&gt;-256 context that allows to iteratively calculate
the hash sum of data larger than an internal boundary size by storing the
algorithms state, among other things, within this struct. This struct&amp;#8217;s
usage, as well as how the Secure Hash Algorithm works, is not explained as
part of this research paper.&lt;/li&gt;
&lt;li&gt;Lines 7 to 15 represent the information stored in a meta data file:&lt;ul&gt;
&lt;li&gt;First of all, each meta data file will contain a version number. This
allows &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; to implement backwards compatibility and makes the
handling of new features to the native &lt;span class="caps"&gt;RAID&lt;/span&gt; implementation much more
flexible.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;unsigned char&lt;/tt&gt; arrays &lt;tt class="docutils literal"&gt;hash_dev0&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;hash_dev1&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;hash_dev2&lt;/tt&gt; are used to store the &lt;span class="caps"&gt;SHA&lt;/span&gt;-256 sum of  the regarding device
files, while &lt;tt class="docutils literal"&gt;hash_in&lt;/tt&gt; stores the hash sum of the original input file.
This makes it possible to verify the integrity of the output file. If any
device file has been compromised, include the corresponding hash sum, the
integrity check of the output file will fail.&lt;/li&gt;
&lt;li&gt;The salt is used to randomize the encryption key and hence increases the
encryption strength.&lt;/li&gt;
&lt;li&gt;Finally the missing element contains information about how many bytes are
missing on the secondary device (not the parity device) to fill the
complete block. This is necessary due to the way &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; handles
files of a size different to the internal &lt;span class="caps"&gt;RAID5&lt;/span&gt; block size.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;sha256_ctx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;buflen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;raid5md&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;hash_dev0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;hash_dev1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;hash_dev2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;hash_in&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ENCRYPTION_SALT_BYTES&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;raid5md&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 2: Structs used for internal storage within &lt;span class="caps"&gt;RAID&lt;/span&gt; level 5
imlementation&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;4.1.3 Function Signatures&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;The The Java Native Interface (&lt;span class="caps"&gt;JNI&lt;/span&gt;)&lt;/strong&gt; implements six functions whereof two
are directly used for the &lt;span class="caps"&gt;RAID&lt;/span&gt; features and three are used to determine some
version and vendor information about the library. The remaining, sixth,
function is used to exchange information about the size of the &lt;tt class="docutils literal"&gt;struct
raid5md&lt;/tt&gt; from the library to &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;. The function signatures are shown
in listing 3 and will be explained here. All &lt;span class="caps"&gt;JNI&lt;/span&gt; functions take at least two
parameters: &lt;tt class="docutils literal"&gt;JNIEnv *env&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;jclass cls&lt;/tt&gt;. The former parameter refers to
the Java environment that provides functions to for example convert and access
strings. The latter is the instance of the current object the &lt;span class="caps"&gt;JNI&lt;/span&gt; function is
working in but is not used within the &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; implementation.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;mergeInterface()&lt;/tt&gt; in lines 1 to 4 is the function that is called to merge
three device files to the output file. It takes the directory where the
device files are stored as first (additional) argument. The file&amp;#8217;s hash
value is taken as second argument. The function will append the file
extensions &lt;tt class="docutils literal"&gt;.0&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;.1&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;.2&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.m&lt;/tt&gt; automatically. The third
parameter defines the full path to the output file. The target directory
must exist, otherwise an error will be returned while trying to open the
output file. The last parameter is the secret part of the key that has been
used during encryption. One must keep in mind, that the
&lt;tt class="docutils literal"&gt;_tempInputDirPath&lt;/tt&gt; must contain a trailing folder separator. On Unix
systems, this is most likely the forward slash (&lt;tt class="docutils literal"&gt;/&lt;/tt&gt;) and on Microsoft
Windows the backward slash (&lt;tt class="docutils literal"&gt;\&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;splitInterface()&lt;/tt&gt;, as defined in lines 5 to 8, is the function signature
for split calls. The function takes four additional parameters: the first
one specifies the base input directory for the file that will be split.  The
second parameter is the path, relative to the first parameter, that directs
to the file. In a multi user environment, adding the user name or any other
user-unique identifier to this relative path will ensure that every output
file exists only once (as long as there are no hash collision for two files
with different user identifiers). The file names for the output files will
be the &lt;span class="caps"&gt;SHA&lt;/span&gt;-256 sum over the concatenation of &lt;tt class="docutils literal"&gt;_inputBasePath&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;_inputFilePath&lt;/tt&gt; appended by &lt;tt class="docutils literal"&gt;.0&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;.1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.2&lt;/tt&gt; for the device
files and &lt;tt class="docutils literal"&gt;.m&lt;/tt&gt; for the meta data file.  The third parameter denotes the
output directory. Finally, &lt;tt class="docutils literal"&gt;_key&lt;/tt&gt; takes the secure part of the encryption
key. In the same manner as in the &lt;tt class="docutils literal"&gt;mergeInterface()&lt;/tt&gt; function, the paths
&lt;tt class="docutils literal"&gt;_inputBasePath&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;_tempOutputDirPath&lt;/tt&gt; must be terminated by folder
separators.&lt;/li&gt;
&lt;li&gt;The functions &lt;tt class="docutils literal"&gt;getName()&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;getVendor()&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;getVersion()&lt;/tt&gt; as defined
in lines 9 to 14 do not take additional parameters. They can be used to show
versioning information of this library that will then exemplarily be shown
on the &lt;span class="caps"&gt;REST&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; information page (c.f. chapter &lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation-continuation/#version-information"&gt;4.2.2 - Version Information&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Lines 15 and 16 show the &lt;tt class="docutils literal"&gt;getMetadataByteLength()&lt;/tt&gt; function that is for
&lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; core internal use only and must return the size of the meta
data file in bytes.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;JNIEXPORT jint JNICALL Java_de_dhbw_1mannheim_cloudraid_core_impl_jni_RaidAccessInterface_mergeInterface(JNIEnv *env, jclass cls,
    jstring _tempInputDirPath, jstring _hash, jstring _outputFilePath,jstring _key);

JNIEXPORT jstring JNICALL Java_de_dhbw_1mannheim_cloudraid_core_impl_jni_RaidAccessInterface_splitInterface(JNIEnv *env, jclass cls,
    jstring _inputBasePath, jstring _inputFilePath, jstring _tempOutputDirPath, jstring _key);

JNIEXPORT jstring JNICALL Java_de_dhbw_1mannheim_cloudraid_core_impl_jni_RaidAccessInterface_getName(JNIEnv *env, jclass cls);

JNIEXPORT jstring JNICALL Java_de_dhbw_1mannheim_cloudraid_core_impl_jni_RaidAccessInterface_getVendor(JNIEnv *env, jclass cls);

JNIEXPORT jstring JNICALL Java_de_dhbw_1mannheim_cloudraid_core_impl_jni_RaidAccessInterface_getVersion(JNIEnv *env, jclass cls);

JNIEXPORT jint JNICALL Java_de_dhbw_1mannheim_cloudraid_core_impl_jni_RaidAccessInterface_getMetadataByteLength(JNIEnv *env, jclass cls);
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 3: &lt;span class="caps"&gt;JNI&lt;/span&gt; header functions signatures.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The main internal functions&lt;/strong&gt; used by the primary two &lt;span class="caps"&gt;JNI&lt;/span&gt; functions above are
explained in this paragraph. For both processes, split and merge, there are two
functions each, &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;split_byte_block()&lt;/tt&gt; (&lt;tt class="docutils literal"&gt;merge_file()&lt;/tt&gt;
and &lt;tt class="docutils literal"&gt;merge_byte_block()&lt;/tt&gt; respectively) that actually handle the processes
(see listing 4 at the end of this post). The &lt;span class="caps"&gt;JNI&lt;/span&gt; functions for split and merge
must be seen as wrapper for the &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;merge_file()&lt;/tt&gt;
functions. These functions handle the complete processes show by the figures 1
and 2 (introduced and explained in a &lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept-continuation/"&gt;previous article&lt;/a&gt; of this article
series). All &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;FILE&lt;/span&gt;*&lt;/tt&gt; parameters must be correctly opened for read or write
access. On Unix systems this includes the binary &lt;tt class="docutils literal"&gt;b&lt;/tt&gt; mode for &lt;tt class="docutils literal"&gt;fopen()&lt;/tt&gt;.&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; expects a file pointer (&lt;tt class="docutils literal"&gt;rb&lt;/tt&gt; mode) to the input file as
first parameter. The &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;FILE&lt;/span&gt; *devices[]&lt;/tt&gt; array must contain three elements
to files opened in &lt;tt class="docutils literal"&gt;wb&lt;/tt&gt; mode. These files are the device files. The
&lt;tt class="docutils literal"&gt;meta&lt;/tt&gt; parameter points to the meta data file as must have been opened in
&lt;tt class="docutils literal"&gt;wb&lt;/tt&gt; mode as well. &lt;tt class="docutils literal"&gt;const char *key&lt;/tt&gt; is the secure key used for file
encryption and will be passed to the &lt;tt class="docutils literal"&gt;hmac()&lt;/tt&gt; function together with its
length &lt;tt class="docutils literal"&gt;keylen&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Split process operation flowchart" src="/images/cloudraid/flowchart-native-split.png" /&gt;
&lt;p class="caption"&gt;&lt;em&gt;Figure 15: The nativ split process&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This function reads up to &lt;tt class="docutils literal"&gt;2 × &lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;&lt;/tt&gt; for each iteration shown in
figure 1. These bytes are encrypted with the key generated by &lt;tt class="docutils literal"&gt;hmac()&lt;/tt&gt; if
&lt;tt class="docutils literal"&gt;ENCRYPT_DATA&lt;/tt&gt; is not 0 and then passed to &lt;tt class="docutils literal"&gt;split_byte_block()&lt;/tt&gt;. The
call-by-reference &amp;#8220;return&amp;#8221; values are written to the device files. After
each iteration the parity position is incremented. Starting with device 2 as
parity for the first block, the parity position will be 0 for the second
block and 1 for the third block. The forth block will restart with position
2.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The &lt;tt class="docutils literal"&gt;split_byte_block()&lt;/tt&gt; function takes the bytes read and encrypted in
&lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; as first parameter and the length of that buffer as second
parameter. The function splits the input character array in three parts that
will be returned via the &lt;tt class="docutils literal"&gt;unsigned char *out&lt;/tt&gt; parameter. The output size
for each device is returned via the fourth parameter: &lt;tt class="docutils literal"&gt;size_t out_len[]&lt;/tt&gt;.
The alignment for the output buffer is as follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The primary device content will be stored at &lt;tt class="docutils literal"&gt;out[0]&lt;/tt&gt;. Its length is
&lt;tt class="docutils literal"&gt;out_len[0]&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;The secondary device content will be stored at &lt;tt class="docutils literal"&gt;out[&lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;]&lt;/tt&gt;.
Its length is &lt;tt class="docutils literal"&gt;out_len[1]&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;The tertiary device content, representing the parity device, will be
stored at &lt;tt class="docutils literal"&gt;out[2 * &lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;]&lt;/tt&gt;. Its length is &lt;tt class="docutils literal"&gt;out_len[2]&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depending on the input length &lt;tt class="docutils literal"&gt;in_len&lt;/tt&gt; the values of &lt;tt class="docutils literal"&gt;out_len[0]&lt;/tt&gt; to
&lt;tt class="docutils literal"&gt;out_len[2]&lt;/tt&gt; may vary:&lt;/p&gt;
&lt;div class="math center"&gt;&lt;img src="/images/equation/887f436188e2c7aae6f1790aa1458191.png"/&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;merge_file()&lt;/tt&gt; is the inverse function to &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt;. It basically
has the same function signature, but the first parameter specifies the
output file unlike in &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; and hence must be opened in &lt;tt class="docutils literal"&gt;wb&lt;/tt&gt;
mode. Thus the device files and meta data file, parameters two and three,
are input files and therefore they must be opened in &lt;tt class="docutils literal"&gt;rb&lt;/tt&gt; mode. The fourth
parameter, &lt;tt class="docutils literal"&gt;const char *key&lt;/tt&gt; is the secure key used for file decryption
and will be passed to the &lt;tt class="docutils literal"&gt;hmac()&lt;/tt&gt; function together with its length
&lt;tt class="docutils literal"&gt;keylen&lt;/tt&gt; in the same way as in &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Merge process operation flowchart" src="/images/cloudraid/flowchart-native-merge.png" /&gt;
&lt;p class="caption"&gt;&lt;em&gt;Figure 16: The native merge process&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;At first, the integrity of the device files is validated as shown in figure
2 by checking the hash sums for the device files. If more than two check
sums are wrong the function terminates with an error. Otherwise the function
will read up to &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;&lt;/tt&gt; from each device file and passes those
buffers with their respective lengths to &lt;tt class="docutils literal"&gt;merge_byte_block()&lt;/tt&gt;. The
call-by-reference &amp;#8220;return&amp;#8221; value from each block merge is passed to a
&lt;span class="caps"&gt;SHA&lt;/span&gt;-256 context to validate the correctness of the output file and then
decrypted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The actual merge occurs in the &lt;tt class="docutils literal"&gt;merge_byte_block()&lt;/tt&gt; function. Again the
content for all three buffers is stored in a single character array as first
parameter: &lt;tt class="docutils literal"&gt;const unsigned char *in&lt;/tt&gt;. The second parameter, &lt;tt class="docutils literal"&gt;const size_t
in_len[]&lt;/tt&gt; represents the lengths for the three read buffers from the device
files. Both parameters together align the input buffers in the same way as
in &lt;tt class="docutils literal"&gt;split_byte_block()&lt;/tt&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The primary device content will be stored at &lt;tt class="docutils literal"&gt;in[0]&lt;/tt&gt;.Its length is
&lt;tt class="docutils literal"&gt;in_len[0]&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;The secondary device content will be stored at &lt;tt class="docutils literal"&gt;in[&lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;]&lt;/tt&gt;. Its
length is &lt;tt class="docutils literal"&gt;in_len[1]&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;The tertiary device content, representing the parity device, will be
stored at &lt;tt class="docutils literal"&gt;in[2 * &lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;]&lt;/tt&gt;. Its length is &lt;tt class="docutils literal"&gt;in_len[2]&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The third parameter, &lt;tt class="docutils literal"&gt;parity_pos&lt;/tt&gt;, specifies which device is the parity.
This is necessary in combination with the fourth parameter, &lt;tt class="docutils literal"&gt;dead_device&lt;/tt&gt;,
to determine the merge strategy: concatenating the content of the buffers
for device files 0 and 1 or using the parity in combination with either of
first two device files. &lt;tt class="docutils literal"&gt;missing&lt;/tt&gt; defines how many bytes are missing on
the secondary device. Only the last block of each overall merge process may
set this parameter to a value &lt;tt class="docutils literal"&gt;&amp;gt; 0&lt;/tt&gt;. The penultimate parameter, &lt;tt class="docutils literal"&gt;*out&lt;/tt&gt;
stores the merged data with a total length of &lt;tt class="docutils literal"&gt;*out_len&lt;/tt&gt; but at most &lt;tt class="docutils literal"&gt;2 ×
&lt;span class="caps"&gt;RAID5BLOCKSIZE&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;create_salt()&lt;/tt&gt;: the complete security of the whole encryption process
relies on the strength of the key given to &lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;merge_file()&lt;/tt&gt;. However, to protect the data against attacks using rainbow
tables the encryption key is being salted. The salt of length
&lt;tt class="docutils literal"&gt;ENCRYPTION_SALT_BYTES&lt;/tt&gt; is generated by this function. On Microsoft
Windows each byte of the salt will be set by a return value of &lt;tt class="docutils literal"&gt;rand()&lt;/tt&gt;
after initializing the pseudo random generator with &lt;tt class="docutils literal"&gt;srand(time(&lt;span class="caps"&gt;NULL&lt;/span&gt;))&lt;/tt&gt;.
On all other &lt;em&gt;Operating Systems&lt;/em&gt; (OSs), that can be expect to be Unix, the
functions reads &lt;tt class="docutils literal"&gt;ENCRYPTION_SALT_BYTES&lt;/tt&gt; from &lt;tt class="docutils literal"&gt;/dev/urandom&lt;/tt&gt; which can be
seen to be random.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The salted key used during encryption and decryption is created by the
function &lt;tt class="docutils literal"&gt;hmac()&lt;/tt&gt;. This function is an implementation of the &lt;em&gt;Federal
Information Processing Standards Publication&lt;/em&gt; (&lt;span class="caps"&gt;FIPS&lt;/span&gt; &lt;span class="caps"&gt;PUB&lt;/span&gt;) 198 [&lt;span class="caps"&gt;NIS02&lt;/span&gt;] &lt;span class="caps"&gt;HMAC&lt;/span&gt;
specification and takes five parameters: the first two parameters (&lt;tt class="docutils literal"&gt;*key&lt;/tt&gt;
and &lt;tt class="docutils literal"&gt;keylen&lt;/tt&gt;) define the secure key given by the &lt;span class="caps"&gt;JNI&lt;/span&gt; function
&lt;tt class="docutils literal"&gt;split_file()&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;merge_file()&lt;/tt&gt;. Parameters three and four link to the
&lt;tt class="docutils literal"&gt;*salt&lt;/tt&gt; (optimally generated by the &lt;tt class="docutils literal"&gt;create_salt()&lt;/tt&gt; function) and its
length that is &lt;tt class="docutils literal"&gt;ENCRYPTION_SALT_BYTES&lt;/tt&gt; by default. The last parameter,
&lt;tt class="docutils literal"&gt;*hash&lt;/tt&gt;, is a call-by-reference parameter and &amp;#8220;returns&amp;#8221; the &lt;span class="caps"&gt;HMAC&lt;/span&gt; for the
given &lt;tt class="docutils literal"&gt;*key&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;*salt&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The general mode of operation can be expressed by the following two
expressions:&lt;/p&gt;
&lt;div class="math center"&gt;&lt;img src="/images/equation/5b75f6a6acf69508ec41005e9aaa634e.png"/&gt;&lt;/div&gt;&lt;p&gt;The implementation of the &lt;span class="caps"&gt;HMAC&lt;/span&gt; &lt;a class="citation-reference" href="#nis02" id="id1"&gt;[&lt;span class="caps"&gt;NIS02&lt;/span&gt;]&lt;/a&gt; standard has been validated by
computing the digest values for the test cases 1, 2, 3, 4, 6 and 7 defined in
&lt;span class="caps"&gt;RFC&lt;/span&gt; 4231 &lt;a class="citation-reference" href="#nys05" id="id2"&gt;[Nys05]&lt;/a&gt;. Test case 5 has not been tested since the truncation is not
needed in &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; and was therefore not implemented.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;split_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;keylen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;split_byte_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;in_len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;out_len&lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;merge_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;keylen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;merge_byte_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;in_len&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;parity_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;dead_device&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;create_salt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;keylen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;saltlen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Listing 4: Internal native header function signatures&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;table class="docutils citation" frame="void" id="nis02" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[&lt;span class="caps"&gt;NIS02&lt;/span&gt;]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="caps"&gt;NIST&lt;/span&gt; Computer Security Division (&lt;span class="caps"&gt;CSD&lt;/span&gt;). The Keyed-Hash Message
Authentication Code (&lt;span class="caps"&gt;HMAC&lt;/span&gt;). Federal Information Processing Standards
Publication &lt;span class="caps"&gt;FIPS&lt;/span&gt; &lt;span class="caps"&gt;PUB&lt;/span&gt; 198, National Institute of Standards and Technology,
March 6, 2002. &lt;a class="reference external" href="http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf"&gt;http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="nys05" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id2"&gt;[Nys05]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Magnus Nystrom. Identifiers and Test Vectors for &lt;span class="caps"&gt;HMAC&lt;/span&gt;-&lt;span class="caps"&gt;SHA&lt;/span&gt;-224,
&lt;span class="caps"&gt;HMAC&lt;/span&gt;-&lt;span class="caps"&gt;SHA&lt;/span&gt;-256, &lt;span class="caps"&gt;HMAC&lt;/span&gt;-&lt;span class="caps"&gt;SHA&lt;/span&gt;-384, and &lt;span class="caps"&gt;HMAC&lt;/span&gt;-&lt;span class="caps"&gt;SHA&lt;/span&gt;-512, December 2005.
&lt;a class="reference external" href="http://tools.ietf.org/html/rfc4231"&gt;http://tools.ietf.org/html/rfc4231&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="C/C++"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 3. Concept (Continuation)</title><link href="https://markusholtermann.eu/2012/11/cloudraid-3-concept-continuation/" rel="alternate"></link><published>2012-11-09T00:00:00+01:00</published><updated>2012-11-09T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-09:/2012/11/cloudraid-3-concept-continuation/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It presents the remaining requirements on the
server and client architecture.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With some delay I can announce the closing chapter for the architectural
backgrounds of &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;. Florian &lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept-continuation/"&gt;published&lt;/a&gt; the rest of chapter &amp;#8220;3.3.
Server Architecture&amp;#8221; and the internals of the &amp;#8220;3.4. Client Application&amp;#8221;.&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 3. Concept</title><link href="https://markusholtermann.eu/2012/11/cloudraid-3-concept/" rel="alternate"></link><published>2012-11-07T00:00:00+01:00</published><updated>2012-11-07T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-07:/2012/11/cloudraid-3-concept/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It presents the requirements, general architecture
and gives some insight on the server architecture.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m happy to announce the next part of our student research paper. Today Florian
&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept/"&gt;presents&lt;/a&gt; the beginning of the third chapter that is all about the concept
behind &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;. His post covers the &amp;#8220;Requirements&amp;#8221; that need to be
fulfilled, the &amp;#8220;General Architecture&amp;#8221; of the server application and also gives
some insight on the &amp;#8220;Server Architecture&amp;#8221;.&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="C/C++"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 2. Basics (Continuation)</title><link href="https://markusholtermann.eu/2012/11/cloudraid-2-basics-continuation/" rel="alternate"></link><published>2012-11-03T00:00:00+01:00</published><updated>2012-11-03T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-11-03:/2012/11/cloudraid-2-basics-continuation/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It describes the technical backgrounds that on
&lt;span class="caps"&gt;RAID&lt;/span&gt; systems.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;2. Basics&lt;/h2&gt;
&lt;h3&gt;2.3. Background on &lt;span class="caps"&gt;RAID&lt;/span&gt; Technology&lt;/h3&gt;
&lt;p&gt;In order to provide data-safety on a common server a &lt;span class="caps"&gt;RAID&lt;/span&gt; can be used. First
introduced in 1988 under the title &lt;em&gt;Redundant Arrays of Inexpensive Disks&lt;/em&gt;
&lt;a class="citation-reference" href="#dk88" id="id1"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/a&gt;, the usage of hard drives in an array collection is state of the art
nowadays. The paper by Petterson, Gibson and Katz introduces the &lt;span class="caps"&gt;RAID&lt;/span&gt; levels 1
to 5 as follows.&lt;/p&gt;
&lt;div class="figure align-right"&gt;
&lt;img alt="RAID 1" src="/images/cloudraid/raid1.png" /&gt;
&lt;p class="caption"&gt;Source: &lt;a class="citation-reference" href="#cbu06a" id="id2"&gt;[Cbu06a]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt; Level 1&lt;/strong&gt; provides a high data-safety with a complete fall-back to a
secondary device. This &amp;#8220;mirroring&amp;#8221; has a space efficiency of 50% of the total
disk capacity &lt;a class="citation-reference" href="#dk88" id="id3"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/a&gt; (ch. 7, p. 112) for two disks. In general the space
efficiency is at &lt;tt class="docutils literal"&gt;1/n&lt;/tt&gt;. The fault tolerance is &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;n-1&lt;/span&gt;&lt;/tt&gt; since all disks
contain the same data and all but the last can fail without any data-loss.&lt;/p&gt;
&lt;div class="figure align-right"&gt;
&lt;img alt="RAID 2" src="/images/cloudraid/raid2.png" /&gt;
&lt;p class="caption"&gt;Source: &lt;a class="citation-reference" href="#kna09" id="id4"&gt;[kna09]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt; Level 2&lt;/strong&gt; defines a bit-level striping with an Error Correction Code
(&lt;span class="caps"&gt;ECC&lt;/span&gt;) for recovery &lt;a class="citation-reference" href="#dk88" id="id5"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/a&gt; (ch. 8, p. 112) that uses the Hamming Code &lt;a class="citation-reference" href="#wik12a" id="id6"&gt;[Wik12a]&lt;/a&gt;
which is stored on multiple check disks. Nowadays &lt;span class="caps"&gt;RAID&lt;/span&gt; systems of level 2 are
not used any more. They are too prohibitive and do not provide more fault
tolerance than &lt;span class="caps"&gt;RAID&lt;/span&gt; level 3, 4 or even 5. &lt;span class="caps"&gt;RAID&lt;/span&gt; level 2 can recover from one
drive failure and has a space efficiency of &lt;a class="citation-reference" href="#wik12b" id="id7"&gt;[Wik12b]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="math center"&gt;&lt;img src="/images/equation/dcb25a750e618dd2d7bad256c1425cdb.png"/&gt;&lt;/div&gt;&lt;div class="figure align-right"&gt;
&lt;img alt="RAID 3" src="/images/cloudraid/raid3.png" /&gt;
&lt;p class="caption"&gt;Source: &lt;a class="citation-reference" href="#cbu06b" id="id8"&gt;[Cbu06b]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt; Level 3&lt;/strong&gt; provides byte-level striping with a single parity per byte
&lt;a class="citation-reference" href="#dk88" id="id9"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/a&gt; (ch. 9, pp. 112f). Taking at least three disks, two for content and one
dedicated for the parity, &lt;span class="caps"&gt;RAID&lt;/span&gt; level 3 provides a space efficiency of &lt;tt class="docutils literal"&gt;1 -
1/n&lt;/tt&gt; and a fault tolerance of one broken disk &lt;a class="citation-reference" href="#wik12b" id="id10"&gt;[Wik12b]&lt;/a&gt;. These statistics for
minimum number of disks, space efficiency and fault tolerance are the same for
&lt;span class="caps"&gt;RAID&lt;/span&gt; level 4 and 5.&lt;/p&gt;
&lt;div class="figure align-right"&gt;
&lt;img alt="RAID 4" src="/images/cloudraid/raid4.png" /&gt;
&lt;p class="caption"&gt;Source: &lt;a class="citation-reference" href="#cbu06c" id="id11"&gt;[Cbu06c]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt; Level 4&lt;/strong&gt;, similar to level 2 and level 3, uses striping to spread the
data over multiple disks. But &lt;span class="caps"&gt;RAID&lt;/span&gt; level 4 splits the data block-wise and not
bit- or byte-wise &lt;a class="citation-reference" href="#dk88" id="id12"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/a&gt; (ch. 10, pp. 113f). This improves &lt;em&gt;Input / Output&lt;/em&gt;
(I/O) performance, but can result in a bottleneck, since the parity is stored
on a single device.&lt;/p&gt;
&lt;div class="figure align-right"&gt;
&lt;img alt="RAID 5" src="/images/cloudraid/raid5.png" /&gt;
&lt;p class="caption"&gt;Source: &lt;a class="citation-reference" href="#cbu06d" id="id13"&gt;[Cbu06d]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt; Level 5&lt;/strong&gt; introduces a distribution of the check disk in order to
resolve the bottleneck that exists in &lt;span class="caps"&gt;RAID&lt;/span&gt; level 4. This does not change any
minimum requirements or fault tolerance, but increases the I/O &lt;a class="citation-reference" href="#dk88" id="id14"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/a&gt; (ch. 11,
p. 114), &lt;a class="citation-reference" href="#wik12b" id="id15"&gt;[Wik12b]&lt;/a&gt; and therefore is one of the most used &lt;span class="caps"&gt;RAID&lt;/span&gt; levels nowadays.&lt;/p&gt;
&lt;h3&gt;2.4. Encryption Standards and Hash Algorithms&lt;/h3&gt;
&lt;p&gt;In modern computer science cryptography is an important topic while planning
and designing software. It is mostly used when data is transferred over an
insecure channel or is stored at a place that cannot be guaranteed to protect
sensitive or confidential data. Encryption algorithms like the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard"&gt;Advanced
Encryption Standard (&lt;span class="caps"&gt;AES&lt;/span&gt;)&lt;/a&gt; and &lt;a class="reference external" href="https://en.wikipedia.org/wiki/RC4"&gt;&lt;span class="caps"&gt;RC4&lt;/span&gt;&lt;/a&gt; (&lt;em&gt;also known as &lt;span class="caps"&gt;ARC4&lt;/span&gt;&lt;/em&gt;) are widely used
and provide strong encryption. The former algorithm is a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Block_cipher"&gt;block cipher&lt;/a&gt; which
means that a certain number of bytes is encrypted simultaneously with the given
key. In contrast, the &lt;span class="caps"&gt;RC4&lt;/span&gt; algorithm is a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Stream_cipher"&gt;stream cipher&lt;/a&gt;, meaning that each
byte is encrypted on its own with respect to the previously encrypted bytes and
the secure key.&lt;/p&gt;
&lt;p&gt;Both named algorithms use the same key for encryption and decryption and
therefore belong to the group of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Symmetric_encryption"&gt;symmetric encryption&lt;/a&gt; algorithms. The &lt;a class="reference external" href="https://en.wikipedia.org/wiki/RSA_(algorithm)"&gt;&lt;span class="caps"&gt;RSA&lt;/span&gt;&lt;/a&gt;
algorithm (see &lt;a class="reference external" href="http://www.google.com/patents/US4405829"&gt;&lt;span class="caps"&gt;U.S.&lt;/span&gt; Patent 4,405,829&lt;/a&gt;) on the other hand belongs to the group
of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Asymmetric_encryption"&gt;asymmetric encryption&lt;/a&gt; algorithms or &lt;em&gt;public-key&lt;/em&gt; algorithms because it
uses a &lt;em&gt;private key&lt;/em&gt; for decryption and a &lt;em&gt;public key&lt;/em&gt; for encryption. One
cannot construct one of these keys form the other without knowing an additional
private information.&lt;/p&gt;
&lt;p&gt;Even if all stated encryption algorithms ensure that the data that has been
encrypted is secure, they cannot provide data integrity and consistency. If an
encrypted message or file is changed, the decryption will return data that
differs from the original data. To eliminate this issue, so called &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Hash_function"&gt;hash
algorithm&lt;/a&gt; are used.  Given some input data, a hash function will return a
&lt;em&gt;hash sum&lt;/em&gt; (also referred as &lt;em&gt;checksum&lt;/em&gt; or only &lt;em&gt;hash&lt;/em&gt;) to this data. Since
hash functions are one-way functions, one cannot get the original data from the
hash sum. A strong hash function will always return a distinct hash sum for a
specific input and does not (or at least tries to minimize) the likelihood of
hash collisions. Hash collisions occur when a hash function returns the same
hash sum for two different input values. This can easily be shown by the
following formula, where &lt;tt class="docutils literal"&gt;x&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;y&lt;/tt&gt; define the input values and &lt;tt class="docutils literal"&gt;H&lt;/tt&gt;
defines the hash function:&lt;/p&gt;
&lt;div class="math center"&gt;&lt;img src="/images/equation/8b7e02d8b7ef9e3fcdc825cd5d6ff596.png"/&gt;&lt;/div&gt;&lt;p&gt;Based on these requirements, the &lt;span class="caps"&gt;NIST&lt;/span&gt; and the German &amp;#8220;Bundesnetzagentur&amp;#8221;
recommend and claim the use of algorithms of the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SHA-2"&gt;Secure Hash Algorithm (&lt;span class="caps"&gt;SHA&lt;/span&gt;)-2
family&lt;/a&gt; &lt;a class="citation-reference" href="#eck12" id="id16"&gt;[Eck12]&lt;/a&gt; with 224, 256, 384 or 512 bits rather than &lt;span class="caps"&gt;MD5&lt;/span&gt; (128 bits) or
&lt;span class="caps"&gt;SHA&lt;/span&gt;-1 (160 bits)&lt;/p&gt;
&lt;h2&gt;Sources&lt;/h2&gt;
&lt;table class="docutils citation" frame="void" id="cbu06a" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id2"&gt;[Cbu06a]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Cburnett. &lt;span class="caps"&gt;RAID&lt;/span&gt; 1. Wikimedia Commons, &lt;span class="caps"&gt;GNU&lt;/span&gt; Free Documentation
License, December 31, 2006.  &lt;a class="reference external" href="http://en.wikipedia.org/wiki/File:RAID_1.svg"&gt;http://en.wikipedia.org/wiki/File:RAID_1.svg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="cbu06b" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id8"&gt;[Cbu06b]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Cburnett. &lt;span class="caps"&gt;RAID&lt;/span&gt; 3. Wikimedia Commons, &lt;span class="caps"&gt;GNU&lt;/span&gt; Free Documentation
License, December 31, 2006.  &lt;a class="reference external" href="http://en.wikipedia.org/wiki/File:RAID_3.svg"&gt;http://en.wikipedia.org/wiki/File:RAID_3.svg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="cbu06c" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id11"&gt;[Cbu06c]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Cburnett. &lt;span class="caps"&gt;RAID&lt;/span&gt; 4. Wikimedia Commons, &lt;span class="caps"&gt;GNU&lt;/span&gt; Free Documentation
License, December 31, 2006.  &lt;a class="reference external" href="http://en.wikipedia.org/wiki/File:RAID_4.svg"&gt;http://en.wikipedia.org/wiki/File:RAID_4.svg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="cbu06d" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id13"&gt;[Cbu06d]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Cburnett. &lt;span class="caps"&gt;RAID&lt;/span&gt; 5. Wikimedia Commons, &lt;span class="caps"&gt;GNU&lt;/span&gt; Free Documentation
License, December 31, 2006.  &lt;a class="reference external" href="http://en.wikipedia.org/wiki/File:RAID_5.svg"&gt;http://en.wikipedia.org/wiki/File:RAID_5.svg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="dk88" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;[&lt;span class="caps"&gt;DK88&lt;/span&gt;]&lt;/td&gt;&lt;td&gt;&lt;em&gt;(&lt;a class="fn-backref" href="#id1"&gt;1&lt;/a&gt;, &lt;a class="fn-backref" href="#id3"&gt;2&lt;/a&gt;, &lt;a class="fn-backref" href="#id5"&gt;3&lt;/a&gt;, &lt;a class="fn-backref" href="#id9"&gt;4&lt;/a&gt;, &lt;a class="fn-backref" href="#id12"&gt;5&lt;/a&gt;, &lt;a class="fn-backref" href="#id14"&gt;6&lt;/a&gt;)&lt;/em&gt; Garth Gibson David A. Patterson and Randy H. Katz. A Case for
Redundant Arrays of Inexpensive Disks (&lt;span class="caps"&gt;RAID&lt;/span&gt;). Technical report, University
of California Berkeley, 1988.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="eck12" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id16"&gt;[Eck12]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Claudia Eckert. &lt;em&gt;&lt;span class="caps"&gt;IT&lt;/span&gt;-Sicherheit – Konzepte – Verfahren – Protokolle&lt;/em&gt;.
Oldenbourg Verlag München, 7 edition, 2012.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="kna09" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id4"&gt;[kna09]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;knakts. &lt;span class="caps"&gt;RAID&lt;/span&gt; 2. Wikimedia Commons, &lt;span class="caps"&gt;GNU&lt;/span&gt; Free Documentation License,
January 18, 2009.  &lt;a class="reference external" href="http://en.wikipedia.org/wiki/File:RAID2_arch.svg"&gt;http://en.wikipedia.org/wiki/File:RAID2_arch.svg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="wik12a" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id6"&gt;[Wik12a]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Wikipedia. Hamming code — Wikipedia, The Free Encyclopedia, January
22, 2012.
&lt;a class="reference external" href="http://en.wikipedia.org/w/index.php?title=Hamming_code&amp;amp;amp;oldid=472688059"&gt;http://en.wikipedia.org/w/index.php?title=Hamming_code&amp;amp;amp;oldid=472688059&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils citation" frame="void" id="wik12b" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;[Wik12b]&lt;/td&gt;&lt;td&gt;&lt;em&gt;(&lt;a class="fn-backref" href="#id7"&gt;1&lt;/a&gt;, &lt;a class="fn-backref" href="#id10"&gt;2&lt;/a&gt;, &lt;a class="fn-backref" href="#id15"&gt;3&lt;/a&gt;)&lt;/em&gt; Wikipedia. &lt;span class="caps"&gt;RAID&lt;/span&gt; — Wikipedia, The Free Encyclopedia, January 25,
2012 &lt;a class="reference external" href="http://en.wikipedia.org/w/index.php?title=RAID&amp;amp;amp;oldid=473130999"&gt;http://en.wikipedia.org/w/index.php?title=&lt;span class="caps"&gt;RAID&lt;/span&gt;&amp;amp;amp;oldid=473130999&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 2. Basics</title><link href="https://markusholtermann.eu/2012/10/cloudraid-2-basics/" rel="alternate"></link><published>2012-10-31T00:00:00+01:00</published><updated>2012-10-31T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-10-31:/2012/10/cloudraid-2-basics/</id><summary type="html">&lt;p class="first last"&gt;This post is a continuation of the blog series about the student
research paper CloudRAID. It describes the technical backgrounds of the
&amp;#8220;OSGi Framework&amp;#8221; and explains the various kinds of &amp;#8220;The Cloud&amp;#8221; that exist.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is a continuation of the blog series about the student research paper
&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;CloudRAID&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today Florian Bausch published &lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-2-basics/"&gt;the next part&lt;/a&gt; of our student seminar paper.
Chapters 2.1. and 2.2. describe the technical backgrounds of the &amp;#8220;OSGi
Framework&amp;#8221; and explain the various kinds of &amp;#8220;The Cloud&amp;#8221; like &lt;em&gt;private cloud&lt;/em&gt;,
&lt;em&gt;public cloud&lt;/em&gt; and &lt;em&gt;hybrid cloud.&lt;/em&gt;&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>[CloudRAID] 1. Introduction</title><link href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/" rel="alternate"></link><published>2012-10-28T00:00:00+02:00</published><updated>2012-10-28T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-10-28:/2012/10/cloudraid-1-introduction/</id><summary type="html">&lt;p class="first last"&gt;During last year a fellow student of mine, Florian Bausch, and I
wrote a student research paper about how to provide availability, redundancy
and security of data in the overall existing and &amp;#8220;well known&amp;#8221; cloud. This is
the first chapter of our papaer.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;During last year a fellow student of mine, &lt;a class="reference external" href="http://blog.fbausch.de"&gt;Florian Bausch&lt;/a&gt;, and I wrote a
student research paper about how to provide availability, redundancy and
security of data in the overall existing and &amp;#8220;well known&amp;#8221; &lt;em&gt;cloud&lt;/em&gt;. In this
context we additionally developed a prototype that we call &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;. The
software is licensed under the terms of the &lt;a class="reference external" href="http://www.apache.org/licenses/LICENSE-2.0.html"&gt;Apache 2 License&lt;/a&gt; and published
on &lt;a class="reference external" href="https://github.com/MarkusH/CloudRAID"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;During the next weeks we are going to publish our paper as a series of posts on
our blogs. This post makes the start of this series and is going to introduce
the general idea behind the topic. The next part of the student research paper
will be published by Florian on his blog and covers some basic topics like the
&amp;#8220;OSGi Framework&amp;#8221; and &amp;#8220;Cloud&amp;#8221;. I will announce the publication here. So, stay
tuned.&lt;/p&gt;
&lt;h2&gt;0. Table of Contents&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/10/cloudraid-1-introduction/"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-2-basics/"&gt;Basics&lt;/a&gt;&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-2-basics/"&gt;The OSGi Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-2-basics/"&gt;Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-2-basics-continuation/#background-on-raid-technology"&gt;Background on &lt;span class="caps"&gt;RAID&lt;/span&gt; Technology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-2-basics-continuation/#encryption-standards-and-hash-algorithms"&gt;Encryption Standards and Hash Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept/"&gt;Concept&lt;/a&gt;&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept/"&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept/"&gt;General Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Server Architecture (&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept/"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept-continuation/"&gt;Part 2&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-3-concept-continuation/"&gt;Client Architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation/"&gt;Implementation&lt;/a&gt;&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation/"&gt;&lt;span class="caps"&gt;JNI&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-4-implementation-continuation/#restful-api-endpoint-specifications"&gt;RESTful &lt;span class="caps"&gt;API&lt;/span&gt; Endpoint Specifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-4-implementation-continuation/"&gt;Compression on RESTful &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-4-implementation-continuation/"&gt;Client Software&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-5-benchmarks/"&gt;Benchmarks&lt;/a&gt;&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-5-benchmarks/#environments"&gt;Environments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-5-benchmarks/#comparison-of-java-python-and-c"&gt;Comparison of Java, Python and C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://markusholtermann.eu/2012/11/cloudraid-5-benchmarks/#raid-level-5-benchmarks"&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt; Level 5 Benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://blog.fbausch.de/cloudraid-6-conclusion-and-outlook/"&gt;Conclusion and Outlook&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;1. Introduction&lt;/h2&gt;
&lt;p&gt;In a world that gets more and more mobile, everybody wants to have access to
his data all the time. The knowledge that one needs an important document and
cannot access it because it is on the computer at home, makes one use cloud
storage providers. But relying on the availability of those cloud providers is
not a good idea as is shown later on. To work around the availability concerns,
one might use multiple cloud storage provides and ends up with a lot of work to
synchronize the storages.&lt;/p&gt;
&lt;div class="figure align-right"&gt;
&lt;img alt="Structural layout idea" class="margin-left" src="/images/cloudraid/structural-idea.png" /&gt;
&lt;p class="caption"&gt;&lt;em&gt;Figure 1: Structural layout idea&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This student research paper by &lt;a class="reference external" href="http://blog.fbausch.de"&gt;Florian Bausch&lt;/a&gt; and &lt;a class="reference external" href="https://markusholtermann.eu"&gt;Markus Holtermann&lt;/a&gt; finds
a solution to this problem. It describes and evaluates the idea, concept and
example implementation of a cloud-based &lt;em&gt;Redundant Array of Independent Disks&lt;/em&gt;
(&lt;span class="caps"&gt;RAID&lt;/span&gt;) storage system. The research goal is a file storage system, that
combines multiple cloud services as back-end storages and represents them as a
single front-end storage to the user. Providing several features like
encryption, authorization, availability and redundancy, the cloud-based &lt;span class="caps"&gt;RAID&lt;/span&gt;
can be used as a reliable backup storage even for sensitive personal
information.&lt;/p&gt;
&lt;p&gt;After introducing the basic knowledge and requirements of the research project,
mainly &lt;em&gt;Open Services Gateway initiative framework&lt;/em&gt; (OSGi), Cloud Computing,
&lt;span class="caps"&gt;RAID&lt;/span&gt; and cryptography, there will be the concept of how to implement the
features and building a cloud-based &lt;span class="caps"&gt;RAID&lt;/span&gt;. This elaboration is then followed by
detailed implementation notes. The program will be titled &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; to
combine the terms &amp;#8220;Cloud&amp;#8221; and &amp;#8220;&lt;span class="caps"&gt;RAID&lt;/span&gt;&amp;#8221; as they are the foundational backgrounds
for the project. To show the validity of &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt;, there are some
benchmarks that point out the advantages and disadvantages as well as the
problems that might occur.&lt;/p&gt;
&lt;p&gt;One must keep in mind that &lt;tt class="docutils literal"&gt;CloudRAID&lt;/tt&gt; will not be a &lt;span class="caps"&gt;RAID&lt;/span&gt; system in the
strict sense, but uses the ideas and techniques of those systems.&lt;/p&gt;
</content><category term="Development"></category><category term="Apache"></category><category term="CloudRAID"></category><category term="Cluster"></category><category term="Eclipse"></category><category term="Encryption"></category><category term="Java"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category><category term="Studium"></category></entry><entry><title>PyCon DE 2012 in Leipzig</title><link href="https://markusholtermann.eu/2012/10/pycon-de-2012-in-leipzig/" rel="alternate"></link><published>2012-10-19T00:00:00+02:00</published><updated>2012-10-19T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-10-19:/2012/10/pycon-de-2012-in-leipzig/</id><summary type="html">&lt;p&gt;At the end of October 2012 another &lt;a class="reference external" href="https://2012.de.pycon.org/"&gt;PyCon&lt;/a&gt; takes
place. This time the venue is the &lt;a class="reference external" href="http://www.leipziger-kubus.ufz.de/"&gt;Kubus in Leipzig, Germany&lt;/a&gt;. As this is my first participation at a Python
conference I&amp;#8217;m a bit excited about this event.&lt;/p&gt;
&lt;p&gt;First of all, I like the &lt;a class="reference external" href="https://2012.de.pycon.org/programm/"&gt;wide range of various topics&lt;/a&gt; that …&lt;/p&gt;</summary><content type="html">&lt;p&gt;At the end of October 2012 another &lt;a class="reference external" href="https://2012.de.pycon.org/"&gt;PyCon&lt;/a&gt; takes
place. This time the venue is the &lt;a class="reference external" href="http://www.leipziger-kubus.ufz.de/"&gt;Kubus in Leipzig, Germany&lt;/a&gt;. As this is my first participation at a Python
conference I&amp;#8217;m a bit excited about this event.&lt;/p&gt;
&lt;p&gt;First of all, I like the &lt;a class="reference external" href="https://2012.de.pycon.org/programm/"&gt;wide range of various topics&lt;/a&gt; that are offered. There are
presentations about &lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/26/"&gt;Machine Learning&lt;/a&gt; (Oct 30 &amp;#64; 1:30pm
in Hall 2) and &lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/20/"&gt;Efficient Data Analysis&lt;/a&gt; (Oct 30 &amp;#64; 2pm in
Hall 1B). Other talks address the topic of computer graphics (&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/56/"&gt;SimpleCV: Fun
with Computer Vision&lt;/a&gt;, Oct 31 &amp;#64; 1:30pm
in Hall 2; &lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/72/"&gt;Tiffany - Reading and Writing Multipage Tiff Files w/ and w/o &lt;span class="caps"&gt;PIL&lt;/span&gt;&lt;/a&gt;, Nov 1 &amp;#64; 11am in
Hall 1A). And of course, there are multiple presentations covering different
parts of the Django web framework: &lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/55/"&gt;&lt;span class="caps"&gt;LFS&lt;/span&gt; - A Django based webshop&lt;/a&gt; (Oct 31 &amp;#64; 2pm in
Hall 1C), &lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/43/"&gt;Overview over the Django admin area&lt;/a&gt; (Oct 31 &amp;#64; 11am in
Hall 1C) &lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/52/"&gt;Django Class Based Generic Views&lt;/a&gt; (Oct 31 &amp;#64; 1pm in
Hall 1C) and many, many more talks.&lt;/p&gt;
&lt;p&gt;Second, while looking through all the talks I just picked some I think I&amp;#8217;m
going to attend:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Tuesday, October 30th&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="first last"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/17/"&gt;Cython with PyPy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/26/"&gt;Machine Learning with Scikit-Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/20/"&gt;Efficient Data Analysis with Pandas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Wednesday, October 31st&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="first last"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/41/"&gt;Fast loops with Cython&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/50/"&gt;Designing and Testing PyZMQ Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Thursday, November 1st&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="first last"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/63/"&gt;Python Web Sprints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/58/"&gt;The Toolbox of a Python (Web) Developer&lt;/a&gt; &lt;em&gt;or&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://2012.de.pycon.org/programm/schedule/sessions/66/"&gt;Agile Database Development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During the barcamp and the sprints on Nov 2nd and 3rd I will work on &lt;a class="reference external" href="http://trac.inyokaproject.org"&gt;Inyoka&lt;/a&gt; and fix a bunch of tickets (the list of open
issues is way too long).&lt;/p&gt;
&lt;p&gt;So, if you are at the PyCon &lt;span class="caps"&gt;DE&lt;/span&gt; 2012 too, leave a comment and we can meet.&lt;/p&gt;
&lt;p&gt;Markus&lt;/p&gt;
</content><category term="Development"></category><category term="Django"></category><category term="PyPy"></category><category term="Python"></category></entry><entry><title>[Update] My own IPv6 tunnel broker.</title><link href="https://markusholtermann.eu/2012/03/my-own-ipv6-tunnel-broker/" rel="alternate"></link><published>2012-03-20T00:00:00+01:00</published><updated>2012-03-20T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2012-03-20:/2012/03/my-own-ipv6-tunnel-broker/</id><summary type="html">&lt;p&gt;Some days ago I needed to test a web application on its IPv6 support. But since
my Internet service provider only gives me an IPv4 address, I was not able to
test the application. So, I thought about registering at &lt;a class="reference external" href="http://www.sixxs.net/"&gt;SixXS&lt;/a&gt; to get an IPv6 address. But then I thought …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Some days ago I needed to test a web application on its IPv6 support. But since
my Internet service provider only gives me an IPv4 address, I was not able to
test the application. So, I thought about registering at &lt;a class="reference external" href="http://www.sixxs.net/"&gt;SixXS&lt;/a&gt; to get an IPv6 address. But then I thought about
setting up my own broker. After some time I found a &lt;a class="reference external" href="https://www.zagbot.com/openvpn_ipv6_tunnel.html"&gt;nearly perfect tutorial&lt;/a&gt; that uses &lt;a class="reference external" href="http://openvpn.net/index.php/open-source.html"&gt;OpenVPN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I took the scripts from that tutorial and stored them on my server and client,
set everything up as explained and &amp;#8230; didn&amp;#8217;t got it working :(. I must say,
that I had limited time to completely understand what was going wrong, so I
stop trying.&lt;/p&gt;
&lt;p&gt;Last weekend I had some hours to work on the IPv6 tunnel broker again and
finally got it working. So, here are my scripts and a short explanation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I did some improvements and enhancements on the scripts and pushed
them to &lt;a class="reference external" href="https://github.com/MarkusH/ipv6-broker"&gt;a Github repository&lt;/a&gt;. Feel
free to fork and report issues.&lt;/p&gt;
&lt;h2&gt;Server&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;You need a dedicated OpenVPN account if your OpenVPN does not run as root.
You must give this user full password-less &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; access to &lt;tt class="docutils literal"&gt;/sbin/ip&lt;/tt&gt;:
&lt;tt class="docutils literal"&gt;openvpn &lt;span class="pre"&gt;&lt;span class="caps"&gt;ALL&lt;/span&gt;=(&lt;span class="caps"&gt;ALL&lt;/span&gt;)&lt;/span&gt;&amp;nbsp; &lt;span class="caps"&gt;NOPASSWD&lt;/span&gt;: /sbin/ip&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;You must enable package forwarding for IPv6. Append
&lt;tt class="docutils literal"&gt;net.ipv6.conf.all.forwarding = 1&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;/etc/sysctl.conf&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;If you automatically receive your IPv6 routes, you &lt;em&gt;must&lt;/em&gt; set your v6
routes manually:&lt;/p&gt;
&lt;div class="last"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ip -6 route add ::/0 via &amp;lt;IPv6-Gateway&amp;gt; dev &amp;lt;eth0&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;You may need to activate &lt;em&gt;Neighbor Discovery Proxy&lt;/em&gt;: Add
&lt;tt class="docutils literal"&gt;net.ipv6.conf.all.proxy_ndp = 1&lt;/tt&gt; to your &lt;tt class="docutils literal"&gt;/etc/sysctl.conf&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Copy the files &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;client-connect-ipv6-broker.sh&lt;/span&gt;&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;client-disconnect-ipv6-broker.sh&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ipv6-broker.sh&lt;/span&gt;&lt;/tt&gt; to your server
into the directory &lt;tt class="docutils literal"&gt;/etc/openvpn/&lt;/tt&gt; and make sure they are executable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Append the lines from the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;server-ipv6.conf&lt;/span&gt;&lt;/tt&gt; file to your OpenVPN server
configuration.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Client&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;You need a dedicated OpenVPN account if your OpenVPN does not run as root.
You must give this user full password-less &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; access to &lt;tt class="docutils literal"&gt;/sbin/ip&lt;/tt&gt;:
&lt;tt class="docutils literal"&gt;openvpn &lt;span class="pre"&gt;&lt;span class="caps"&gt;ALL&lt;/span&gt;=(&lt;span class="caps"&gt;ALL&lt;/span&gt;)&lt;/span&gt;&amp;nbsp; &lt;span class="caps"&gt;NOPASSWD&lt;/span&gt;: /sbin/ip&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Copy the files &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;up-ipv6-broker.sh&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;down-ipv6-broker.sh&lt;/span&gt;&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ipv6-broker.sh&lt;/span&gt;&lt;/tt&gt; to your client into the directory &lt;tt class="docutils literal"&gt;/etc/openvpn/&lt;/tt&gt; and
make sure they are executable.&lt;/li&gt;
&lt;li&gt;Append the lines from the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;client-ipv6.conf&lt;/span&gt;&lt;/tt&gt; file to your OpenVPN client
configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The scripts&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;OpenVPN IPv6 Tunnel Broker

Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="m"&gt;2012&lt;/span&gt; Markus Holtermann

Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="m"&gt;2011&lt;/span&gt; Lyndsay Roger - https://www.zagbot.com/

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version &lt;span class="m"&gt;3&lt;/span&gt; of the License, or &lt;span class="o"&gt;(&lt;/span&gt;at your option&lt;span class="o"&gt;)&lt;/span&gt; any later
version.

This program is distributed &lt;span class="k"&gt;in&lt;/span&gt; the hope that it will be useful, but WITHOUT ANY
WARRANTY&lt;span class="p"&gt;;&lt;/span&gt; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License &lt;span class="k"&gt;for&lt;/span&gt; more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see http://www.gnu.org/licenses/.
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;ipv6-broker.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c1"&gt;# Your /64 Network prefix&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;BASERANGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1234:5678:90ab:cdef&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;# The IPv4 address of your vpn server&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;VPN_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;10.8.0.1&amp;quot;&lt;/span&gt;

&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;LOG_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ipv6-broker&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;quot;/etc/openvpn/client.conf&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;CLIENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$CLIENT&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sit1&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASERANGE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;::&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk -F. &lt;span class="s1"&gt;&amp;#39;{printf &amp;quot;%02x%02x:%02x&amp;quot;, $2, $3, $4}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sit&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk -F. &lt;span class="s1"&gt;&amp;#39;{print $2&amp;quot;-&amp;quot;$3&amp;quot;-&amp;quot;$4}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASERANGE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;::&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk -F. &lt;span class="s1"&gt;&amp;#39;{printf &amp;quot;%02x%02x:%02x&amp;quot;, $2, $3, $4}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;client-connect-ipv6-broker.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; /etc/openvpn/ipv6-broker.sh

&lt;span class="c1"&gt;# This is a script that is run each time a remote client connects&lt;/span&gt;
&lt;span class="c1"&gt;# to this openvpn server.&lt;/span&gt;
&lt;span class="c1"&gt;# it will setup the ipv6 tunnel depending on the ip address that was&lt;/span&gt;
&lt;span class="c1"&gt;# given to the client&lt;/span&gt;

&lt;span class="c1"&gt;# setup the sit between the local and remote openvpn addresses&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip tunnel add &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mode sit ttl 255 remote &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; local &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip tunnel add &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; mode sit ttl &lt;span class="m"&gt;255&lt;/span&gt; remote &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# activate the tunnel device&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip link set dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; up&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip link &lt;span class="nb"&gt;set&lt;/span&gt; dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; up

&lt;span class="c1"&gt;# config routing for the new network&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 addr add &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;01/124 dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 addr add &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;/124 dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# add the route for the network&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 route add &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;00/124 via &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;02 dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; metric 1&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 route add &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;/124 via &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt; dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; metric &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# add neighbor discovering proxy for this interface&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 neigh add proxy &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;02 dev eth0&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 neigh add proxy &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt; dev eth0

&lt;span class="c1"&gt;# log to syslog&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;script_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; client_ip:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;trusted_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; common_name:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;common_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; local_ip:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; remote_ip:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; sit:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ipv6net:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip addr show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip -6 route show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;

&lt;span class="c1"&gt;# needed for connection&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;client-disconnect-ipv6-broker.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; /etc/openvpn/ipv6-broker.sh

&lt;span class="c1"&gt;# remove neighbor discovering proxy again&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 neigh del proxy &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;02 dev eth0&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 neigh del proxy &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt; dev eth0

&lt;span class="c1"&gt;# remove the route&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 route del &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;00/124 via &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;02 dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; metric 1&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 route del &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;/124 via &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt; dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; metric &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# unset the ipv6 address&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 addr del &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;01/124 dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 addr del &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;/124 dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# deactivate the tunnel&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip link set dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; down&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip link &lt;span class="nb"&gt;set&lt;/span&gt; dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; down

&lt;span class="c1"&gt;# remove the tunnel interface&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip tunnel del &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mode sit ttl 255 remote &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; local &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip tunnel del &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; mode sit ttl &lt;span class="m"&gt;255&lt;/span&gt; remote &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_pool_remote_ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip addr show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip -6 route show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;

&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;up-ipv6-broker.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; /etc/openvpn/ipv6-broker.sh

&lt;span class="c1"&gt;# script that is run on the client when it creates a tunnel to the remote OpenVPN server&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip tunnel add &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mode sit ttl 255 remote &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VPN_HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; local &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip tunnel add &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; mode sit ttl &lt;span class="m"&gt;255&lt;/span&gt; remote &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VPN_HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip link set dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; up&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip link &lt;span class="nb"&gt;set&lt;/span&gt; dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; up

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 addr add &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;02/124 dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 addr add &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;/124 dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip route add ::/0 via &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;01&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip route add ::/0 via &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip addr show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip -6 route show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;

&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;down-ipv6-broker.sh&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; /etc/openvpn/ipv6-broker.sh

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip route del ::/0 via &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;01&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip route del ::/0 via &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip -6 addr del &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;02/124 dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip -6 addr del &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;V6NET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="m"&gt;02&lt;/span&gt;/124 dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip link set dev &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; down&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip link &lt;span class="nb"&gt;set&lt;/span&gt; dev &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; down

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/sbin/ip tunnel del &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mode sit ttl 255 remote &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VPN_HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; local &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
sudo /sbin/ip tunnel del &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; mode sit ttl &lt;span class="m"&gt;255&lt;/span&gt; remote &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VPN_HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ifconfig_local&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip addr show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nv"&gt;$DEBUG&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo /sbin/ip -6 route show &lt;span class="p"&gt;|&lt;/span&gt; /usr/bin/logger -t &lt;span class="nv"&gt;$LOG_TAG&lt;/span&gt;

&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Configuration&lt;/p&gt;
&lt;p&gt;You finally need to add a few lines to your &lt;tt class="docutils literal"&gt;/etc/openvpn/server.conf&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;###########################################&lt;/span&gt;
&lt;span class="c1"&gt;# IPv6 tunnel broker&lt;/span&gt;
&lt;span class="c1"&gt;###########################################&lt;/span&gt;
script-security &lt;span class="m"&gt;2&lt;/span&gt;
client-connect /etc/openvpn/client-connect-ipv6-broker.sh
client-disconnect /etc/openvpn/client-disconnect-ipv6-broker.sh
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And a few lines to your &lt;tt class="docutils literal"&gt;/etc/openvpn/client.conf&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;#############################################&lt;/span&gt;
&lt;span class="c1"&gt;# IPv6 tunnel broker&lt;/span&gt;
&lt;span class="c1"&gt;#############################################&lt;/span&gt;
&lt;span class="c1"&gt;# need this so when the client disconnects it&lt;/span&gt;
&lt;span class="c1"&gt;# tells the server so the server can remove&lt;/span&gt;
&lt;span class="c1"&gt;# the ipv6 tunnel the client was using&lt;/span&gt;
explicit-exit-notify
script-security &lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="c1"&gt;# create the ipv6 tunnel&lt;/span&gt;
up /etc/openvpn/up-ipv6-broker.sh
down /etc/openvpn/down-ipv6-broker.sh
&lt;/pre&gt;&lt;/div&gt;
</content><category term="Linux"></category><category term="Bash"></category><category term="Encryption"></category><category term="Linux"></category><category term="Network"></category><category term="Security"></category><category term="Server"></category></entry><entry><title>Mandelbrot in Python</title><link href="https://markusholtermann.eu/2011/11/mandelbrot-in-python/" rel="alternate"></link><published>2011-11-17T00:00:00+01:00</published><updated>2011-11-17T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2011-11-17:/2011/11/mandelbrot-in-python/</id><summary type="html">&lt;p class="first last"&gt;I just found a nice Python script that draws the Mandelbrot set. Have
a look!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I just &lt;a class="reference external" href="http://preshing.com/20110926/high-resolution-mandelbrot-in-obfuscated-python"&gt;found&lt;/a&gt;
a nice Python script that draws the &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Mandelbrot_set"&gt;Mandelbrot set&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;                                      &lt;span class="o"&gt;=&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;
                                        &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="k"&gt;lambda&lt;/span&gt;
                               &lt;span class="n"&gt;V&lt;/span&gt;       &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;
                             &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;   &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;c&lt;/span&gt;
                               &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
               &lt;span class="p"&gt;(&lt;/span&gt;              &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**-&lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;
                 &lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
                  &lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;  &lt;span class="nn"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;\
            &lt;span class="n"&gt;j&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;amp;lt;QIIHHHH&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M.bmp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;BM&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;i&lt;/span&gt;  &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;BBB&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
                  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;950&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;  &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;880&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;701&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
                 &lt;span class="n"&gt;T&lt;/span&gt;  &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;     &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))(&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;              &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;3.&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
                               &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;3.&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;2.5&lt;/span&gt;
                             &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;  \
                               &lt;span class="n"&gt;A&lt;/span&gt;       &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;
                                      &lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
                                        &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                       &lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can change the output dimension in line 8. Just make sure that the width is
dividable by 4.&lt;/p&gt;
&lt;p&gt;I ran the script with a resolution of 3000x2000 px on my T400 with an Intel
Core2Duo P8600 &amp;#64; 2.40GHz and &lt;span class="caps"&gt;4GB&lt;/span&gt; &lt;span class="caps"&gt;RAM&lt;/span&gt;. I used Python 2.7.2-2 on Arch Linux 64bit
and it took took me half an our. And as stated in the article as well, PyPy
1.6-2 should be much faster. And I can confirm that. It only runs half the time
of the regular Python implementation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ &lt;span class="nb"&gt;time&lt;/span&gt; python2 mandelbrot.py
real    29m16.779s
user    29m12.519s
sys     0m0.977s

$ &lt;span class="nb"&gt;time&lt;/span&gt; pypy mandelbrot.py
real    16m21.750s
user    16m18.200s
sys     0m1.320s
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And here the download link. I converted the file M.bmp, which had a size of &lt;span class="caps"&gt;18MB&lt;/span&gt;
to &lt;span class="caps"&gt;PNG&lt;/span&gt; with a size of 1.&lt;span class="caps"&gt;7MB&lt;/span&gt;:&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/mandelbrot.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/mandelbrot-315x315.jpg" alt="KDE 4.10 RC1" title="KDE 4.10 RC1"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/mandelbrot-364x364.jpg" alt="KDE 4.10 RC1" title="KDE 4.10 RC1"&gt;&lt;img class="small lazyload" data-src="/images/thumb/mandelbrot-480x480.jpg" alt="KDE 4.10 RC1" title="KDE 4.10 RC1"&gt;&lt;/a&gt;&lt;/div&gt;</content><category term="Development"></category><category term="Arch Linux"></category><category term="Linux"></category><category term="PyPy"></category><category term="Python"></category></entry><entry><title>Dominator 1.0 released</title><link href="https://markusholtermann.eu/2011/11/dominator-10-released/" rel="alternate"></link><published>2011-11-14T00:00:00+01:00</published><updated>2011-11-14T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2011-11-14:/2011/11/dominator-10-released/</id><summary type="html">&lt;p&gt;As a result of my fourth semester at the university, my team and I created the
&lt;a class="reference external" href="https://markusholtermann.eu/de/2011/05/neues-vom-studium/"&gt;&lt;span class="caps"&gt;DOMINATOR&lt;/span&gt;&lt;/a&gt;
(de) program, an &lt;a class="reference external" href="http://en.wikipedia.org/wiki/OpenGL#OpenGL_2.1"&gt;OpenGL 2.1&lt;/a&gt;
based 3D domino simulation. You can find a demo video at &lt;a class="reference external" href="http://www.youtube.com/watch?v=H2vHt1vh1Sg"&gt;YouTube&lt;/a&gt;.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/dominator-1.0-screenshot.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/dominator-1.0-screenshot-315x315.jpg" alt="Dominator 1.0 Screenshot" title="Dominator 1.0 Screenshot"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/dominator-1.0-screenshot-364x364.jpg" alt="Dominator 1.0 Screenshot" title="Dominator 1.0 Screenshot"&gt;&lt;img class="small lazyload" data-src="/images/thumb/dominator-1.0-screenshot-480x480.jpg" alt="Dominator 1.0 Screenshot" title="Dominator 1.0 Screenshot"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;In round about 13,000 lines of C++ code we solved this exercise. As a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;As a result of my fourth semester at the university, my team and I created the
&lt;a class="reference external" href="https://markusholtermann.eu/de/2011/05/neues-vom-studium/"&gt;&lt;span class="caps"&gt;DOMINATOR&lt;/span&gt;&lt;/a&gt;
(de) program, an &lt;a class="reference external" href="http://en.wikipedia.org/wiki/OpenGL#OpenGL_2.1"&gt;OpenGL 2.1&lt;/a&gt;
based 3D domino simulation. You can find a demo video at &lt;a class="reference external" href="http://www.youtube.com/watch?v=H2vHt1vh1Sg"&gt;YouTube&lt;/a&gt;.&lt;/p&gt;
&lt;div class="row gallery"&gt;
&lt;a class="col s12 m6 l4" href="/images/dominator-1.0-screenshot.png"&gt;
&lt;img class="large lazyload" data-src="/images/thumb/dominator-1.0-screenshot-315x315.jpg" alt="Dominator 1.0 Screenshot" title="Dominator 1.0 Screenshot"&gt;&lt;img class="medium lazyload" data-src="/images/thumb/dominator-1.0-screenshot-364x364.jpg" alt="Dominator 1.0 Screenshot" title="Dominator 1.0 Screenshot"&gt;&lt;img class="small lazyload" data-src="/images/thumb/dominator-1.0-screenshot-480x480.jpg" alt="Dominator 1.0 Screenshot" title="Dominator 1.0 Screenshot"&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;In round about 13,000 lines of C++ code we solved this exercise. As a &lt;span class="caps"&gt;UI&lt;/span&gt;
framework we decided to make use of the &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Qt_(framework)"&gt;Qt framework&lt;/a&gt; which has perfect OpenGL
integration. The &lt;a class="reference external" href="https://code.google.com/p/lib3ds/"&gt;lib3ds&lt;/a&gt; and the &lt;a class="reference external" href="http://newtondynamics.com/"&gt;Newton
Game Dynamics&lt;/a&gt; are used to import &lt;span class="caps"&gt;3DS&lt;/span&gt; models and
calculate the physical environment. Several &lt;a class="reference external" href="https://github.com/MarkusH/dominator/wiki/Third-Party-Libraries"&gt;3rd-party libraries&lt;/a&gt; finalize
the set of external software that we use for our project.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;DOMINATOR&lt;/span&gt; 1.0 officially supports Microsoft Windows 7 and Ubuntu 10.04 (Lucid
Lynx) and 10.10 (Maverick Meerkat). I tested it on Arch Linux as well and even
developed it on my Arch Linux systems. But there is no install script for Arch
right now, sorry for that, but you can easily compile it from source. See the
links below and have a brief look at the &lt;a class="reference external" href="https://github.com/MarkusH/dominator/wiki/Installation"&gt;development installation docs&lt;/a&gt;. In theory Mac &lt;span class="caps"&gt;OSX&lt;/span&gt;
should be supported as well, but we couldn&amp;#8217;t get Newton Game Dynamics compiled.&lt;/p&gt;
&lt;p&gt;And here&amp;#8217;s &lt;a class="reference external" href="http://github.com/MarkusH/dominator"&gt;the code on Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Markus&lt;/p&gt;
</content><category term="Development"></category><category term="Arch Linux"></category><category term="C/C++"></category><category term="Debian"></category><category term="Eclipse"></category><category term="Git"></category><category term="KDE"></category><category term="Linux"></category><category term="Studium"></category><category term="Ubuntu"></category><category term="Windows"></category></entry><entry><title>My own Git server - installing gitolite</title><link href="https://markusholtermann.eu/2011/09/my-own-git-server-installing-gitolite/" rel="alternate"></link><published>2011-09-16T00:00:00+02:00</published><updated>2011-09-16T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2011-09-16:/2011/09/my-own-git-server-installing-gitolite/</id><summary type="html">&lt;p class="first last"&gt;Yesterday I wanted to share some code with some friends, but I didn&amp;#8217;t
want to create a repository on &lt;a class="reference external" href="https://github.com"&gt;GitHub&lt;/a&gt;, so I decided to install my own git
server. After some minutes googleing I decided on gitolite.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Yesterday I wanted to share some code with some friends, but I didn&amp;#8217;t want to
create a repository on &lt;a class="reference external" href="https://github.com"&gt;GitHub&lt;/a&gt;, so I decided to install
my own git server. After some minutes googleing I found &lt;a class="reference external" href="http://eagain.net/gitweb/?p=gitosis.git"&gt;gitosis&lt;/a&gt; and
&lt;a class="reference external" href="https://github.com/sitaramc/gitolite"&gt;gitolite&lt;/a&gt;. Both programs seem to be popular, but the last commit to &lt;em&gt;gitosis&lt;/em&gt;
was two yeas ago. So I chose &lt;em&gt;gitolite&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I had two options on how to install &lt;em&gt;gitolite&lt;/em&gt;: either through the package
manager (&lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;apt-get&lt;/span&gt; install gitolite&lt;/tt&gt;) or by directly cloning the
repository from Github. I used the latter one to be up to date.&lt;/p&gt;
&lt;p&gt;I wanted &lt;em&gt;gitolite&lt;/em&gt; to run under its own user account, lets say &lt;tt class="docutils literal"&gt;gitolite&lt;/tt&gt;.
So I had to add this user:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;markus@server:~$ sudo adduser --system &lt;span class="se"&gt;\&lt;/span&gt;
--shell /bin/bash &lt;span class="se"&gt;\&lt;/span&gt;
--group &lt;span class="se"&gt;\&lt;/span&gt;
--disabled-password &lt;span class="se"&gt;\&lt;/span&gt;
--home /home/gitolite &lt;span class="se"&gt;\&lt;/span&gt;
gitolite
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The installation was quite easy. For initial configuration I needed my &lt;span class="caps"&gt;SSH&lt;/span&gt;-key.
So I copied my local &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.ssh/id_rsa.pub&lt;/span&gt;&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;/tmp/markus.pub&lt;/tt&gt; on the
server. Then I switched to the newly created user and cloned the code from
Github:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;markus@server:~$ sudo su gitolite -
gitolite@server:/home/markus$ &lt;span class="nb"&gt;cd&lt;/span&gt;
gitolite@server:~$ git clone https://github.com/sitaramc/gitolite.git
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After these steps I could install &lt;em&gt;gitolite&lt;/em&gt;. Therefor I ran those commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gitolite@server:~$ &lt;span class="nb"&gt;cd&lt;/span&gt; gitolite
gitolite@server:~$ src/gl-system-install
gitolite@server:~$ gl-setup /tmp/markus.pub
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now I was able to clone the repository to my local machine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git clone gitolite@server:gitolite-admin
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A new folder &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gitolite-admin&lt;/span&gt;&lt;/tt&gt; was created that contains a configuration file
and an directory for &lt;span class="caps"&gt;SSH&lt;/span&gt;-keys. To add my other computers and my friends to the
repositories, I changed the configuration file as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="na"&gt;@markus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;markus markus2 markus3&lt;/span&gt;
&lt;span class="na"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;user1 user2&lt;/span&gt;
&lt;span class="na"&gt;@otheruser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;otheruser1 otheruser2 otheruser3&lt;/span&gt;

&lt;span class="na"&gt;repo gitolite-admin&lt;/span&gt;
     &lt;span class="na"&gt;RW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@markus&lt;/span&gt;

&lt;span class="na"&gt;repo project1&lt;/span&gt;
     &lt;span class="na"&gt;RW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@markus @user otheruser2&lt;/span&gt;

&lt;span class="na"&gt;repo project2&lt;/span&gt;
     &lt;span class="na"&gt;RW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@markus @user&lt;/span&gt;
     &lt;span class="na"&gt;R&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@otheruser&lt;/span&gt;

&lt;span class="na"&gt;repo project3&lt;/span&gt;
     &lt;span class="na"&gt;RW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@markus&lt;/span&gt;
     &lt;span class="na"&gt;R&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@all&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first three lines declare some groups, so if one gets a new client, I can
add that to the group and he will directly have access to all repositories that
his other clients can access as well.&lt;/p&gt;
&lt;p&gt;The repository &lt;em&gt;gitolite-admin&lt;/em&gt; is only readable (&lt;tt class="docutils literal"&gt;R&lt;/tt&gt;) and writabel (&lt;tt class="docutils literal"&gt;W&lt;/tt&gt;)
by myself. The &lt;em&gt;project1&lt;/em&gt; has read/write permissions for all my clients, all
clients of &lt;em&gt;user&lt;/em&gt; but only the second client of &lt;em&gt;otheruser&lt;/em&gt;. In the second
project repository, (&lt;em&gt;project2&lt;/em&gt;) all clients of &lt;em&gt;otheruser&lt;/em&gt; can only read, but
not write. Finally, repository &lt;em&gt;project3&lt;/em&gt; is only writable for all my clients
but readable for &lt;strong&gt;all&lt;/strong&gt; users.&lt;/p&gt;
&lt;p&gt;The next step was to add the changes and all new &lt;span class="caps"&gt;SSH&lt;/span&gt;-keys to the repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ git add conf/gitolite.conf keydir/markus2.pub keydir/markus3.pub &lt;span class="se"&gt;\&lt;/span&gt;
keydir/user1.pub keydir/user2.pub &lt;span class="se"&gt;\&lt;/span&gt;
keydir/otheruser1.pub keydir/otheruser2.pub keydir/otheruser3.pub
$ git commit -m &lt;span class="s1"&gt;&amp;#39;add first projects and users&amp;#39;&lt;/span&gt;
$ git push
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ready.&lt;/p&gt;
&lt;p&gt;If you are now trying to connect via &lt;span class="caps"&gt;SSH&lt;/span&gt; and user &lt;tt class="docutils literal"&gt;gitolite&lt;/tt&gt; to the server,
you will get a nice information:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ ssh git@server
hello markus, this is gitolite running on git
the gitolite config gives you the following access:
     R   W      gitolite-admin
     R   W      project1
     R   W      project2
     R   W      project3
Connection to server closed.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thanks for reading :)&lt;/p&gt;
&lt;p&gt;Markus&lt;/p&gt;
</content><category term="Linux"></category><category term="Debian"></category><category term="Git"></category><category term="Linux"></category><category term="Server"></category></entry><entry><title>pyChallenge 1.0 veröffentlicht</title><link href="https://markusholtermann.eu/de/2011/07/pychallenge-10-veroffentlicht/" rel="alternate"></link><published>2011-07-22T00:00:00+02:00</published><updated>2011-07-22T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2011-07-22:/de/2011/07/pychallenge-10-veroffentlicht/</id><summary type="html">&lt;p&gt;Wie in meinem Artikel vom 26.05.2011 &lt;a class="reference external" href="https://markusholtermann.eu/de/2011/05/neues-vom-studium/"&gt;angekündigt&lt;/a&gt;, habe ich mit
einigen Kommilitonen in der Datenbankvorlesung das Projekt &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge"&gt;pyChallenge&lt;/a&gt; entwickelt mit dem die Spielstärken
von Schach- oder Tennisspielern berechnet werden können. Dies geschieht anhand
der Algorithmen &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Elo-Zahl"&gt;&lt;span class="caps"&gt;ELO&lt;/span&gt;&lt;/a&gt; und &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Glicko-System"&gt;Glicko&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Weiterhin lassen sich mit &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge"&gt;pyChallenge&lt;/a&gt; auch die Besten- und Schlechtesten- …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Wie in meinem Artikel vom 26.05.2011 &lt;a class="reference external" href="https://markusholtermann.eu/de/2011/05/neues-vom-studium/"&gt;angekündigt&lt;/a&gt;, habe ich mit
einigen Kommilitonen in der Datenbankvorlesung das Projekt &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge"&gt;pyChallenge&lt;/a&gt; entwickelt mit dem die Spielstärken
von Schach- oder Tennisspielern berechnet werden können. Dies geschieht anhand
der Algorithmen &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Elo-Zahl"&gt;&lt;span class="caps"&gt;ELO&lt;/span&gt;&lt;/a&gt; und &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Glicko-System"&gt;Glicko&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Weiterhin lassen sich mit &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge"&gt;pyChallenge&lt;/a&gt; auch die Besten- und Schlechtesten-
Listen eines Spiels ausgeben, und die bestmöglichen Paarungen für einen jeden
Spieler errechnen.&lt;/p&gt;
&lt;p&gt;Kern der gesamten Entwicklung lag aber auf dem &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Objektrelationale_Abbildung"&gt;&lt;span class="caps"&gt;ORM&lt;/span&gt;&lt;/a&gt;, welches wir für
unsere Zwecke selber entwickelt haben. Das &lt;span class="caps"&gt;ORM&lt;/span&gt; nimmt uns nun folgende
Funktionen ab wodurch die Anwendungslogik sehr gut von der Datenrepräsentation
abgetrennt wird:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;INSERT&lt;/span&gt; &lt;span class="caps"&gt;INTO&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SELECT&lt;/span&gt; &lt;span class="caps"&gt;FROM&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;UPDATE&lt;/span&gt; &lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;DELETE&lt;/span&gt; &lt;span class="caps"&gt;FROM&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CREATE&lt;/span&gt; &lt;span class="caps"&gt;TABLE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;TRUNCATE&lt;/span&gt; &lt;span class="caps"&gt;TABLE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;DROP&lt;/span&gt; &lt;span class="caps"&gt;TABLE&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ein kurzes Beispiel, wie das &lt;span class="caps"&gt;ORM&lt;/span&gt; genutzt wird, ist &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge/blob/master/pychallenge/db/models.py#L17-L63"&gt;dort&lt;/a&gt; zu sehen.&lt;/p&gt;
&lt;p&gt;Dann bleibt uns nur noch Danke für das Testen und Nutzen zu sagen.&lt;/p&gt;
&lt;p&gt;Ein Artikel zu dem Projekt &lt;em&gt;Dominator&lt;/em&gt; aus der Software-Engineering Vorlesung,
folgt demnächst.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Folge uns auf &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge/"&gt;github.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Development"></category><category term="Django"></category><category term="Python"></category><category term="SQLite"></category><category term="Studium"></category></entry><entry><title>Neues vom Studium</title><link href="https://markusholtermann.eu/de/2011/05/neues-vom-studium/" rel="alternate"></link><published>2011-05-26T00:00:00+02:00</published><updated>2011-05-26T00:00:00+02:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2011-05-26:/de/2011/05/neues-vom-studium/</id><summary type="html">&lt;p&gt;Das neue Semester hat Anfang des Monats begonnen und es stehen wieder Projekte
an. Im letzten Semester habe ich mit meinem Team das Programm &lt;a class="reference external" href="https://github.com/MarkusH/pxChat"&gt;pxChat&lt;/a&gt; entwicket. Dieses Mal müssen jedoch zwei
Projekte gestemmt werden.&lt;/p&gt;
&lt;p&gt;In der Vorlesung &lt;em&gt;Software Engineering&lt;/em&gt; entwickeln wir einen &lt;a class="reference external" href="https://github.com/MarkusH/dominator/"&gt;3D-Domino
Simulator&lt;/a&gt;. Dabei setzen wir auf
C/C …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Das neue Semester hat Anfang des Monats begonnen und es stehen wieder Projekte
an. Im letzten Semester habe ich mit meinem Team das Programm &lt;a class="reference external" href="https://github.com/MarkusH/pxChat"&gt;pxChat&lt;/a&gt; entwicket. Dieses Mal müssen jedoch zwei
Projekte gestemmt werden.&lt;/p&gt;
&lt;p&gt;In der Vorlesung &lt;em&gt;Software Engineering&lt;/em&gt; entwickeln wir einen &lt;a class="reference external" href="https://github.com/MarkusH/dominator/"&gt;3D-Domino
Simulator&lt;/a&gt;. Dabei setzen wir auf
C/C++, Qt und OpenGL sowie einige andere Bibliotheken. Eigentlich wollten wir
das Programm ausschließlich für Linux programmieren, aber unser Dozent will
eine Windows-Version haben :-| . Daher hat sich ein Kommilitone bereits an die
Portierung der ersten paar 1000 Zeilen Quelltext gemacht und hat schon Stunden
benötigt. Hoffen wir also, dass bald die komplizierten Bibliotheken portiert
sind und die Entwicklung dann effizient fortschreiten kann.&lt;/p&gt;
&lt;p&gt;Als Projekt der Vorlesung &lt;em&gt;Datenbanken&lt;/em&gt; werden wir &lt;a class="reference external" href="https://github.com/MarkusH/pyChallenge"&gt;pyChallenge&lt;/a&gt; entwickeln. Dieses in Python
geschriebene Programm ist zum &amp;#8220;Vorhersagen&amp;#8221; von Spielergebnissen. Solche
Vorhersagen erfolgen bspw. mittels &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Elo-Zahl"&gt;&lt;span class="caps"&gt;ELO&lt;/span&gt;&lt;/a&gt; oder &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Glicko-System"&gt;Glicko&lt;/a&gt;. Die
nötigen statistischen Daten werden wir in einer SQLite Datenbank ablegen. Der
Zugriff auf diese Daten soll dann über ein kleines selbst entwickeltes &lt;a class="reference external" href="http://de.wikipedia.org/wiki/Objektrelationale_Abbildung"&gt;&lt;span class="caps"&gt;ORM&lt;/span&gt;&lt;/a&gt; erfolgen.&lt;/p&gt;
&lt;p&gt;So, das wäre es dann erstmal.&lt;/p&gt;
&lt;p&gt;Markus&lt;/p&gt;
</content><category term="Development"></category><category term="C/C++"></category><category term="Python"></category><category term="SQLite"></category><category term="Studium"></category></entry><entry><title>Binärtaschenrechner mit Flex und Bison</title><link href="https://markusholtermann.eu/de/2010/12/flex-und-bison/" rel="alternate"></link><published>2010-12-14T00:00:00+01:00</published><updated>2010-12-14T00:00:00+01:00</updated><author><name>Markus Holtermann</name></author><id>tag:markusholtermann.eu,2010-12-14:/de/2010/12/flex-und-bison/</id><summary type="html">&lt;p class="first last"&gt;Einen Taschenrechner für Binärzahlen kann man mit Flex und Bison
recht schnell erstellen.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Einen Taschenrechner für Binärzahlen kann man mit &lt;a class="reference external" href="http://flex.sourceforge.net/"&gt;Flex&lt;/a&gt; und &lt;a class="reference external" href="http://www.gnu.org/software/bison/"&gt;Bison&lt;/a&gt; recht schnell erstellen. Zunächst
erstellen wir eine Datei &lt;em&gt;calc_binary.l&lt;/em&gt;. Diese ist der sogenannte Lexer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;quot;calc_binary.y.h&amp;quot;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;%%&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PLUS&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MAL&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NU&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EINS&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AUF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ZU&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;what?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Diese Datei übersetzen wir dann mit:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ flex -o calc_binary.c calc_binary.l
&lt;/pre&gt;
&lt;p&gt;Nun folgt der Parser, ein Bison-File, mit dem Namen &lt;em&gt;calc_binary.y&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;math.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;yyerror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;

&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;AUS&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;PLUS&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;MAL&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;NU&lt;/span&gt; &lt;span class="n"&gt;EINS&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="n"&gt;AUF&lt;/span&gt; &lt;span class="n"&gt;ZU&lt;/span&gt;

&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="n"&gt;PLUS&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="n"&gt;MAL&lt;/span&gt;

&lt;span class="o"&gt;%%&lt;/span&gt;
&lt;span class="nl"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;AUS&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;= %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;AUS&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;=%d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;);};&lt;/span&gt;

&lt;span class="nl"&gt;E&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;PLUS&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;$3&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;$3&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;MAL&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;$3&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;AUF&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;ZU&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MINUS&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;$2&lt;/span&gt;&lt;span class="p"&gt;;};&lt;/span&gt;

&lt;span class="nl"&gt;B&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NU&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;EINS&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="n"&gt;NU&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="n"&gt;EINS&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;$$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;};&lt;/span&gt;

&lt;span class="o"&gt;%%&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;yyparse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;yyerror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Good bye&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Auch diesen müssen wir übersetzen:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ bison -d -b y -o calc_binary.y.c calc_binary.y
&lt;/pre&gt;
&lt;p&gt;Nun können wir mit &lt;span class="caps"&gt;GCC&lt;/span&gt; eine ausführbare Datei erstellen können:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ gcc calc_binary.c calc_binary.y.c -lfl -lm -o calc_binary
&lt;/pre&gt;
&lt;p&gt;Wenn wir diese mit:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ ./calc_binary
&lt;/pre&gt;
&lt;p&gt;aufrufen, können wir Binärzahlen addieren, subtrahieren und multiplizieren.&lt;/p&gt;
</content><category term="Development"></category><category term="Bison"></category><category term="Flex"></category></entry></feed>