<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/django/</id>
  <link href="https://www.endpointdev.com/blog/tags/django/"/>
  <link href="https://www.endpointdev.com/blog/tags/django/" rel="self"/>
  <updated>2025-02-06T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Django and Mojolicious: a quick comparison of two popular web frameworks</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/02/django-mojolicious/"/>
      <id>https://www.endpointdev.com/blog/2025/02/django-mojolicious/</id>
      <published>2025-02-06T00:00:00+00:00</published>
      <author>
        <name>Marco Pessotto</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/02/django-mojolicious/architecture-structure-wood-building-beam-construction-1063818-pxhere.webp&#34; alt=&#34;A view upward toward the wooden framing of a house under construction against a blue sky.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo https://pxhere.com/en/photo/1063818 CC0 Public Domain --&gt;
&lt;p&gt;Recently I&amp;rsquo;ve been working on a project with a &lt;a href=&#34;https://vuejs.org/&#34;&gt;Vue&lt;/a&gt; front-end and two back-ends, one in Python using the &lt;a href=&#34;https://www.djangoproject.com/&#34;&gt;Django&lt;/a&gt; framework and one in Perl using the &lt;a href=&#34;https://www.mojolicious.org/&#34;&gt;Mojolicious&lt;/a&gt; framework. So, it&amp;rsquo;s a good time to spend some words to share the experience and do a quick comparison.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2022/04/perl-web-frameworks/&#34;&gt;Previously&lt;/a&gt; I wrote a post about Perl web frameworks, and now I&amp;rsquo;m expanding the subject into another language.&lt;/p&gt;
&lt;p&gt;Django was chosen for this project because it&amp;rsquo;s been around for almost 20 years now and provides the needed maturity and stability to be long-running and low-budget. In this regard, it has proved a good choice so far. Recently it saw a major version upgrade without any problems to speak of. It could be argued that I should have used the &lt;a href=&#34;https://github.com/encode/django-rest-framework&#34;&gt;Django REST Framework&lt;/a&gt; instead of plain Django. However, at the time the decision was made, adding a framework on top of another seemed a bit excessive. I don&amp;rsquo;t have many regrets about this, though.&lt;/p&gt;
&lt;p&gt;Mojolicious is an old acquaintance. It used to have fast-paced development but seems very mature now, and it&amp;rsquo;s even been &lt;a href=&#34;https://mojojs.org/&#34;&gt;ported&lt;/a&gt; to JavaScript.&lt;/p&gt;
&lt;p&gt;Both frameworks have just a few dependencies (which is fairly normal in the Python world, but not in the Perl one) and excellent documentation. They both follow the model-view-controller pattern. Let&amp;rsquo;s examine the components.&lt;/p&gt;
&lt;h3 id=&#34;views&#34;&gt;Views&lt;/h3&gt;
&lt;p&gt;Both frameworks come with a built-in template system (which can be swapped out with something else), but in this project we can skip the topic altogether as both frameworks are used only as back-end for transmitting JSON, without any HTML rendering involved.&lt;/p&gt;
&lt;p&gt;However, let&amp;rsquo;s see how the rendering looks for the API we&amp;rsquo;re writing.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Base&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Mojolicious::Controller&amp;#39;&lt;/span&gt;, -signatures;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;check&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;render(json =&amp;gt; { status =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;OK&amp;#39;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.http&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; JsonResponse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;status&lt;/span&gt;(request):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; JsonResponse({ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;:  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;OK&amp;#34;&lt;/span&gt; })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nothing complicated here, just provide the right call.&lt;/p&gt;
&lt;h3 id=&#34;models&#34;&gt;Models&lt;/h3&gt;
&lt;h4 id=&#34;django&#34;&gt;Django&lt;/h4&gt;
&lt;p&gt;Usually a model in context of web development means a database and here we are going to keep this assumption.&lt;/p&gt;
&lt;p&gt;Django comes with a comprehensive &lt;a href=&#34;https://docs.djangoproject.com/en/5.1/topics/db/queries/&#34;&gt;object-relational mapping&lt;/a&gt; (ORM) system and it feels like the natural thing to use. I don&amp;rsquo;t think it makes much sense to use another ORM, or even to use raw SQL queries (though it is &lt;a href=&#34;https://docs.djangoproject.com/en/5.1/topics/db/sql/&#34;&gt;possible&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;You usually start a Django project by defining the model. The Django ORM gives you the tools to manage the migrations, providing abstraction from the SQL. You need to define the field types and the relationships (joins and foreign keys) using the appropriate class methods.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; models
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;User&lt;/span&gt;(AbstractUser):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email = models.EmailField(null=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    site = models.ForeignKey(Site, on_delete=models.CASCADE, related_name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;site_users&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    libraries = models.ManyToManyField(Library, related_name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;affiliated_users&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expiration = models.DateTimeField(null=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created = models.DateTimeField(auto_now_add=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_modified = models.DateTimeField(auto_now=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These calls provide not only the SQL type to use, but also the validation. For example, the &lt;code&gt;blank&lt;/code&gt; parameter is a validation option specifying whether Django will accept an empty value. It is different from the &lt;code&gt;null&lt;/code&gt; option, which directly correlates to SQL. You can see we&amp;rsquo;re quite far from working with SQL, at least two layers of abstraction away.&lt;/p&gt;
&lt;p&gt;In the example above, we&amp;rsquo;re also defining a foreign key between a site and a user (many-to-one), so each user belongs to one site. We also define a many-to-many relationship with the libraries record. I like how these relationships are defined, it&amp;rsquo;s very concise.&lt;/p&gt;
&lt;p&gt;Thanks to these definitions, you get a whole &lt;a href=&#34;https://docs.djangoproject.com/en/5.1/ref/contrib/admin/&#34;&gt;admin console&lt;/a&gt; almost for free, which your admin users are sure to like. However, I&amp;rsquo;m not sure this is a silver bullet for solving all problems. With large tables and relationships the admin pages load slowly and they could become unusable very quickly. Of course, you can tune that by filtering out what you need and what you don&amp;rsquo;t, but that means things are not as simple as &amp;ldquo;an admin dashboard for free&amp;rdquo; — at the very least, there&amp;rsquo;s some configuring to do.&lt;/p&gt;
&lt;p&gt;As for the query syntax, you usually need to call &lt;code&gt;Class.objects.filter()&lt;/code&gt;. As you would expect from an ORM, you can chain the calls and finally get objects out of that, representing a database row, which, in turn, you can update or delete.&lt;/p&gt;
&lt;p&gt;The syntax for the &lt;code&gt;filter()&lt;/code&gt; call is based on the double underscore separator, so you can query over the relationships like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; agent &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; (Agent.objects.filter(canonical_agent_id__isnull=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              .prefetch_related(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;canonical_agent&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              .order_by(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;canonical_agent__name&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              .all()):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent.name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Dummy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent.save()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, provided that we defined the foreign keys and the attributes in the model, we can search/​order across the relationship. The &lt;code&gt;__isnull&lt;/code&gt; suffix, as you can imagine, results in a &lt;code&gt;WHERE canonical_agent_id IS NOT NULL&lt;/code&gt; query, while in the &lt;code&gt;order_by&lt;/code&gt; call we sort over the joined table using the &lt;code&gt;name&lt;/code&gt; column. Looks nice and readable, with a touch of magic.&lt;/p&gt;
&lt;p&gt;Of course things are never so simple, so you can build complex queries with the &lt;code&gt;Q&lt;/code&gt; class combined with bytewise operators (&lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of a simple case-insensitive search for a name containing multiple words:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db.models&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; Q
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;api_list&lt;/span&gt;(request)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    term = request.GET.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;search&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; term
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        words = [ w &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; w &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; re.split(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;\W+&amp;#39;&lt;/span&gt;, term) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; w ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; words:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            query = Q(name__icontains=words.pop())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;while&lt;/span&gt; words:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                query = query &amp;amp; Q(name__icontains=words.pop())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#888&#34;&gt;# logger.debug(query)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            agents = Agent.objects.filter(query).all()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To sum up, the ORM is providing everything you need to stay away from the SQL. In fact, it seems like Django doesn&amp;rsquo;t like you doing raw SQL queries.&lt;/p&gt;
&lt;h4 id=&#34;mojolicious-and-perl&#34;&gt;Mojolicious and Perl&lt;/h4&gt;
&lt;p&gt;In the Perl world things are a bit different.&lt;/p&gt;
&lt;p&gt;The Mojolicious &lt;a href=&#34;https://docs.mojolicious.org/Mojolicious/Guides/Tutorial&#34;&gt;tutorial&lt;/a&gt; doesn&amp;rsquo;t even mention the database. You can use any ORM or no ORM at all, if you prefer so. However, Mojolicious makes the DB handle available everywhere in the application.&lt;/p&gt;
&lt;p&gt;You could use &lt;a href=&#34;https://metacpan.org/pod/DBIx::Connector&#34;&gt;DBIx::Connector&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/pod/DBIx::Class&#34;&gt;DBIx::Class&lt;/a&gt;, &lt;a href=&#34;https://docs.mojolicious.org/Mojo/Pg&#34;&gt;Mojo::Pg&lt;/a&gt; (which was developed with Mojolicious), or whatever you prefer.&lt;/p&gt;
&lt;p&gt;For example, to use Mojo::Pg in the main application class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Base&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Mojolicious&amp;#39;&lt;/span&gt;, -signatures;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Pg&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Data::Dumper::Concise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;startup&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;plugin(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NotYAMLConfig&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;log&lt;/span&gt;-&amp;gt;info(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Starting up with &amp;#34;&lt;/span&gt; . Dumper(&lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;helper(pg =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                      state &lt;span style=&#34;color:#369&#34;&gt;$pg&lt;/span&gt; = &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Mojo::Pg&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt;-&amp;gt;{dbi_connection_string});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the routes you can call &lt;code&gt;$self-&amp;gt;pg&lt;/code&gt; to get the database object.&lt;/p&gt;
&lt;p&gt;The three approaches I&amp;rsquo;ve mentioned here are different.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DBIx::Connector&lt;/code&gt; is basically a way to get you a safe DBI handle across forks and DB connection failures.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Mojo::Pg&lt;/code&gt; gives you the ability to do abstract queries but also gives some convenient methods to get the results. I wouldn&amp;rsquo;t call it a ORM; from a query you usually gets hashes, not objects, you don&amp;rsquo;t need to define the database layout, and it won&amp;rsquo;t produce migrations for you, though there is some &lt;a href=&#34;https://docs.mojolicious.org/Mojo/Pg/Migrations&#34;&gt;migration support&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of standard and abstract queries:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;list_texts&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sid&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;param(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sid&amp;#39;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SELECT * FROM texts WHERE sid = ? ORDER BY sorting_index&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;@all&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pg&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;db&lt;/span&gt;-&amp;gt;query(&lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$sid&lt;/span&gt;)-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hashes&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;each&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;render(json =&amp;gt; { texts =&amp;gt; \&lt;span style=&#34;color:#369&#34;&gt;@all&lt;/span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The query above can be rewritten with an abstract query, using the same module.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;@all&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;pg&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;db&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;select&lt;/span&gt;(texts =&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             { sid =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$sid&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                             { order_by =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sorting_index&amp;#39;&lt;/span&gt; })-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;hashes&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#038&#34;&gt;each&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If it&amp;rsquo;s a simple, static query, it&amp;rsquo;s basically a matter of taste; do you prefer to see the SQL or not? The second version is usually nicer if you want to build a different query depending on the parameters, so you add or remove keys to the hashes which maps to query and finally execute it.&lt;/p&gt;
&lt;p&gt;Now, speaking of taste, for complex queries with a lot of joins I honestly prefer to see the SQL query instead of wondering if the abstract one is producing the correct SQL. This is true regardless of the framework. I have the impression that it is faster, safer, and cleaner to have the explicit SQL in the code rather than leaving future developers (including future me) to wonder if the magic is happening or not.&lt;/p&gt;
&lt;p&gt;Finally, nothing stops you from using &lt;code&gt;DBIx::Class&lt;/code&gt;, which is the best ORM for Perl, even if it&amp;rsquo;s not exactly light on dependencies.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s very versatile, it can build queries of arbitrary complexity, and you usually get objects out of the queries you make. It doesn&amp;rsquo;t come with an admin dashboard, it doesn&amp;rsquo;t enforce the data types and it doesn&amp;rsquo;t ship any validation by default (of course, you can implement that manually). The query syntax is very close to the &lt;code&gt;Mojo::Pg&lt;/code&gt; one (which is basically &lt;a href=&#34;https://metacpan.org/pod/SQL::Abstract&#34;&gt;SQL::Abstract&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The gain here is that, like in Django&amp;rsquo;s ORM, you can attach your methods to the classes representing the rows, so the data definitions live with the code operating on them.&lt;/p&gt;
&lt;p&gt;However, the fact that it builds an object for each result means you&amp;rsquo;re paying a performance penalty which sometimes can be very high. I think this is a problem common to all ORMs, regardless of the language and framework you&amp;rsquo;re using.&lt;/p&gt;
&lt;p&gt;The difference with Django is that once you have chosen it as your framework, you are basically already sold to the ORM. With Mojolicious and other Perl frameworks (Catalyst, Dancer), you can still make the decision and, at least in theory, change it down the road.&lt;/p&gt;
&lt;p&gt;My recommendation would be to keep the model, both code and business logic, decoupled from the web-specific code. This is not really doable with Django, but is fully doable with the Perl frameworks. Just put the DB configuration in a dedicated file and the business code in appropriate classes. Then you should be able to, for example, run a script without loading the web and the whole framework configuration. In this ideal scenario, the web framework just provides the glue between the user and your model.&lt;/p&gt;
&lt;h3 id=&#34;controllers&#34;&gt;Controllers&lt;/h3&gt;
&lt;p&gt;Routes are defined similarly between Django and Mojolicious. Usually you put the code in a class and then point to it, attaching a name to it so you can reference it elsewhere. The language is different, the style is different, but they essentially do the same thing.&lt;/p&gt;
&lt;p&gt;Django:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.urls&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; path
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; views
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;api/agents/&amp;lt;int:agent_id&amp;gt;&amp;#34;&lt;/span&gt;, views.api_agent_view, name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;api_agent_view&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The function &lt;code&gt;views.api_agent_view&lt;/code&gt; will receive the request with the &lt;code&gt;agent_id&lt;/code&gt; as a parameter.&lt;/p&gt;
&lt;p&gt;Mojolicious:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;startup&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;# ....&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;routes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt;-&amp;gt;get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/list/:sid&amp;#39;&lt;/span&gt;)-&amp;gt;to(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;API#list_texts&amp;#39;&lt;/span&gt;)-&amp;gt;name(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;api_list_texts&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-&amp;gt;to&lt;/code&gt; method is routing the request to the &lt;code&gt;Myapp::Controller::API::list_texts&lt;/code&gt;, which will receive the request with the &lt;code&gt;sid&lt;/code&gt; as parameter.&lt;/p&gt;
&lt;p&gt;This is pretty much the core business of every web framework: routing a request to a given function.&lt;/p&gt;
&lt;p&gt;Mojolicious has also the ability to &lt;a href=&#34;https://docs.mojolicious.org/Mojolicious/Guides/Routing#Under&#34;&gt;chain the routes&lt;/a&gt; (pretty much taken from Catalyst). The typical use is authorization:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;startup&lt;/span&gt; ($self) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$self&lt;/span&gt;-&amp;gt;routes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;my&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$api&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$r&lt;/span&gt;-&amp;gt;under(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/api/v1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;sub&lt;/span&gt; ($c) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;req&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;headers&lt;/span&gt;-&amp;gt;header(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;X-API-Key&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080&#34;&gt;eq&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;testkey&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$c&lt;/span&gt;-&amp;gt;render(text =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Authentication required!&amp;#39;&lt;/span&gt;, status =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;401&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;undef&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$api&lt;/span&gt;-&amp;gt;get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/check&amp;#39;&lt;/span&gt;)-&amp;gt;to(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;API#check&amp;#39;&lt;/span&gt;)-&amp;gt;name(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;api_check&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So the request to &lt;code&gt;/api/v1/check&lt;/code&gt; will first go in the first block and the chain will abort if the API key is not set in the header. Otherwise it will proceed to run the &lt;code&gt;API&lt;/code&gt; module&amp;rsquo;s &lt;code&gt;check&lt;/code&gt; function.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m Perl guy and so I&amp;rsquo;m a bit biased toward Mojolicious, but I also have a pragmatic approach to programming. Python is widely used — they teach it in schools — while Perl is seen as old-school, if not dead (like all the mature technologies). So, Python could potentially attract more developers to your project, and this is important to consider.&lt;/p&gt;
&lt;p&gt;Learning a new language like Python is not a big leap; it and Perl are quite similar despite the different syntax. I&amp;rsquo;d throw Ruby in the same basket.&lt;/p&gt;
&lt;p&gt;Of course both languages provide high quality modules you can use, and these two frameworks are an excellent example.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to deploy a containerized Django app with AWS Copilot</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/"/>
      <id>https://www.endpointdev.com/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/</id>
      <published>2022-06-21T00:00:00+00:00</published>
      <author>
        <name>Jeffry Johar</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/pilots.webp&#34; alt=&#34;Photo of 2 pilots in an airplane cockpit&#34;&gt;&lt;/p&gt;
&lt;!-- Photo licensed under CC0 (public domain) from https://pxhere.com/en/photo/609377 --&gt;
&lt;p&gt;Generally there are 2 major options at AWS when it comes to deployment of containerized applications. You can either go for EKS or ECS.&lt;/p&gt;
&lt;p&gt;EKS (Elastic Kubernetes Service) is the managed Kubernetes service by AWS. ECS (Elastic Container Service), on the other hand, is AWS&amp;rsquo;s own way to manage your containerized application. You can learn more about EKS and ECS &lt;a href=&#34;https://aws.amazon.com/blogs/containers/amazon-ecs-vs-amazon-eks-making-sense-of-aws-container-services/&#34;&gt;on the AWS website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For this post we will use ECS.&lt;/p&gt;
&lt;h3 id=&#34;the-chosen-one-and-the-sidekick&#34;&gt;The chosen one and the sidekick&lt;/h3&gt;
&lt;p&gt;With ECS chosen, now you have to find a preferably easy way to deploy your containerized application on it.&lt;/p&gt;
&lt;p&gt;There are quite a number of resources from AWS that are needed for your application to live on ECS, such as VPC (Virtual Private Cloud), Security Group (firewall), EC2 (virtual machine), Load Balancer, and others. Creating these resources manually is cumbersome so AWS has came out with a tool that can automate the creation of all of them. The tool is known as AWS Copilot and we are going to learn how to use it.&lt;/p&gt;
&lt;h3 id=&#34;install-docker&#34;&gt;Install Docker&lt;/h3&gt;
&lt;p&gt;Docker or Docker Desktop is required for building the Docker image later. Please refer to my previous article on &lt;a href=&#34;/blog/2022/06/getting-started-with-docker-and-kubernetes-on-macos/&#34;&gt;how to install Docker Desktop on macOS&lt;/a&gt;, or &lt;a href=&#34;https://docs.docker.com/get-docker/&#34;&gt;follow Docker&amp;rsquo;s instructions&lt;/a&gt; for Linux and Windows.&lt;/p&gt;
&lt;h3 id=&#34;set-up-aws-cli&#34;&gt;Set up AWS CLI&lt;/h3&gt;
&lt;p&gt;We need to set up the Docker AWS CLI (command-line interface) for authentication and authorization to AWS.&lt;/p&gt;
&lt;p&gt;Execute the following command to install the AWS CLI on macOS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ curl -O &amp;#34;https://awscli.amazonaws.com/AWSCLIV2.pkg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo installer -pkg AWSCLIV2.pkg -target /&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For other OSes see &lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&#34;&gt;Amazon&amp;rsquo;s docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Execute the following command and enter the &lt;a href=&#34;https://docs.aws.amazon.com/powershell/latest/userguide/pstools-appendix-sign-up.html&#34;&gt;AWS Account and Access Keys&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ aws configure&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;install-aws-copilot-cli&#34;&gt;Install AWS Copilot CLI&lt;/h3&gt;
&lt;p&gt;Now it&amp;rsquo;s time for the main character: AWS Copilot.&lt;/p&gt;
&lt;p&gt;Install AWS Copilot with Homebrew for macOS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ brew install aws/tap/copilot-cli&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;See &lt;a href=&#34;https://aws.github.io/copilot-cli/docs/getting-started/install/&#34;&gt;AWS Copilot Installation&lt;/a&gt; for other platforms.&lt;/p&gt;
&lt;h3 id=&#34;the-django-project&#34;&gt;The Django project&lt;/h3&gt;
&lt;p&gt;Create a Django project by using a Python Docker Image. You can clone my Git project to get the &lt;code&gt;Dockerfile&lt;/code&gt;, &lt;code&gt;docker-compose.yaml&lt;/code&gt; and &lt;code&gt;requirements.txt&lt;/code&gt; that I&amp;rsquo;m using.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ git clone https://github.com/aburayyanjeffry/django-copilot.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Go to the &lt;code&gt;django-pilot&lt;/code&gt; directory and execute &lt;code&gt;docker-compose&lt;/code&gt; to create a Django project named &amp;ldquo;mydjango&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd django-copilot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker-compose run web django-admin startproject mydjango .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Edit &lt;code&gt;mydjango/settings.py&lt;/code&gt; to allow all hostnames for its URL. This is required because by default AWS will generate a random URL for the application. Find the following variable and set the value as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ALLOWED_HOSTS = [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-deployment-with-aws-copilot&#34;&gt;The Deployment with AWS Copilot&lt;/h3&gt;
&lt;p&gt;Create an AWS Copilot &amp;ldquo;Application&amp;rdquo;. This is a grouping of services such as web app or database, environments (development, QA, production), and CI/CD pipelines. Execute the following command to create an Application with the name of &amp;ldquo;mydjango&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ copilot init -a mydjango&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Select the Workload type. Since this Django is an internet-facing app we will choose the &amp;ldquo;Load Balanced Web Service&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Which workload type best represents your architecture?  [Use arrows to move, type to filter, ? for more help]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Request-Driven Web Service  (App Runner)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;gt; Load Balanced Web Service   (Internet to ECS on Fargate)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Backend Service             (ECS on Fargate)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Worker Service              (Events to SQS to ECS on Fargate)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Scheduled Job               (Scheduled event to State Machine to Fargate)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Give the Workload a name. We are going to name it &amp;ldquo;mydjango-web&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Workload type: Load Balanced Web Service
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  What do you want to name this service? [? for help] mydjango-web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Select the Dockerfile in the current directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Which Dockerfile would you like to use for mydjango-web?  [Use arrows to move, type to filter, ? for more help]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;gt; ./Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Enter custom path for your Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Use an existing image instead&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Accept to create a test environment.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;All right, you&amp;#39;re all set for local development.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Would you like to deploy a test environment? [? for help] (y/N) y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wait and see. At the end of the deployment you will get the URL of your application. Open it in a browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/sample.webp&#34; alt=&#34;Sample output of AWS copilot init run&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/browser.webp&#34; alt=&#34;Sample view from a browser of a Django app default debug page stating &amp;ldquo;The install worked successfully! Congratulations!&amp;rdquo;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s migrate some data, create a superuser, and try to log in. The Django app comes with a SQLite database. Execute the following command to get a terminal for the Django app:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ copilot svc exec&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once in the terminal, execute the following to migrate the initial data and to create the superuser:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ python manage.py migrate
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ python manage.py createsuperuser&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&#34;/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/sample-db.webp&#34; alt=&#34;Output from Django database migration run&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now you may access the admin page and login by using the created credentials.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/login01.webp&#34; alt=&#34;Django login page screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;You should see the Django admin:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2022/06/how-to-deploy-containerized-django-app-with-aws-copilot/login02.webp&#34; alt=&#34;Django admin page screenshot after successful login&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;a-mini-cheat-sheet&#34;&gt;A mini cheat sheet&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;AWS Copilot commands&lt;/th&gt;
          &lt;th&gt;Remarks&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;copilot app ls&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;To list available Applications&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;copilot app show -n appname&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;To get the details of an Application&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;copilot app delete -n appname&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;To delete an Application&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;copilot svc ls&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;To list available Services&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;copilot svc show -n svcname&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;To get the details of a Service&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;copilot svc delete -n svcname&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;To delete a Service&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;the-end&#34;&gt;The End&lt;/h3&gt;
&lt;p&gt;That&amp;rsquo;s all, folks.&lt;/p&gt;
&lt;p&gt;AWS Copilot is a tool to automate the deployment of AWS infrastructure for our containerized application needs. It takes away most of the worries about infrastructure and enables us to focus sooner on the application development.&lt;/p&gt;
&lt;p&gt;For further info on AWS Copilot &lt;a href=&#34;https://aws.github.io/copilot-cli/&#34;&gt;visit its website&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>GraphQL Server Libraries</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/07/graphql-server-libraries/"/>
      <id>https://www.endpointdev.com/blog/2019/07/graphql-server-libraries/</id>
      <published>2019-07-12T00:00:00+00:00</published>
      <author>
        <name>Zed Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/07/graphql-server-libraries/image-0.jpg&#34; alt=&#34;Eroded Icelandic mountain&#34; /&gt;&lt;br&gt;Photo by &lt;a href=&#34;https://unsplash.com/photos/t07FAEn9wAA&#34;&gt;Jon Flobrant&lt;/a&gt; on Unsplash&lt;/p&gt;
&lt;p&gt;This post is a followup to my previous post, &lt;a href=&#34;/blog/2019/05/graphql-an-alternative-to-rest/&#34;&gt;GraphQL — An Alternative to REST&lt;/a&gt;. Please check that out for an introduction to GraphQL and what makes it different from other API solutions. I’ve collected a list of some of the currently-maintained GraphQL libraries for a few different languages, along with some examples (most of which aren’t fully functional on their own, they’d need more configuration) so you can see what it might be like to use GraphQL in your project. I’ll be focusing on the ways each of these libraries implement GraphQL and what you’d need to do to start a project with each of them, so if you have questions about GraphQL itself, please check out my other blog post.&lt;/p&gt;
&lt;h3 id=&#34;apollo-server-javascripttypescript&#34;&gt;Apollo Server (JavaScript/TypeScript)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.apollographql.com/&#34;&gt;Apollo GraphQL&lt;/a&gt; has libraries for both a GraphQL server and client (which I’ll discuss later). &lt;a href=&#34;https://www.apollographql.com/docs/apollo-server/&#34;&gt;Apollo Server&lt;/a&gt; can be used both as a standalone server as well as with libraries like &lt;a href=&#34;https://expressjs.com/&#34;&gt;Express&lt;/a&gt;. Apollo Server is the server library I have the most experience with—I wrote a server last year using Express and Apollo Server, along with a client that used Apollo Client. I’m a fan of the flexibiliy of Apollo, but it takes more work to set up than some of the alternatives.&lt;/p&gt;
&lt;p&gt;Setting up Apollo Server as a standalone can be done fairly simply following the directions on &lt;a href=&#34;https://www.apollographql.com/docs/apollo-server/getting-started/&#34;&gt;their website&lt;/a&gt;. However, I’m going to go over the basics of integrating with Express. There are two main parts to writing a server with Apollo: your GraphQL schema and your resolvers. These stay more or less the same whether you’re using Apollo as a standalone server or combining it with Express. You do have to have a database set up separately; I’ll show examples with MongoDB, but you could easily swap it out with PostgreSQL or another database. I’ll show an example resolver along with its GraphQL schema for a blog post. The schema follows the GraphQL schema rules and might look like the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; typeDefs = [gql&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  type Post {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    id: String!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    body: String!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  query {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    post(id: String!): Post
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`&lt;/span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now for the resolver. Resolvers are functions that take information from the query (like arguments) and return the relevant data, usually from a database. For our blog post, a resolver might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; resolvers = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  post: (root, args, context, info) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; Post.findById(args.id);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Simple! We just get the data from the database and return it—as long as the property names match those of our schema, Apollo will automatically format it according to the frontend’s request and return it to them.&lt;/p&gt;
&lt;p&gt;OK, next we create the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; express from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;express&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; { ApolloServer } from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;apollo-server-express&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; PORT = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3000&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; app = express();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; server = ApolloServer({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  typeDefs,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  resolvers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server.applyMiddleware(app);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app.listen(PORT, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  console.log(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`Server running at http://localhost:&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;${&lt;/span&gt;PORT&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/graphql`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that’s it! Note that these examples are missing a few things like imports, and we didn’t add authentication of any kind, but this is the general format for creating a server with Apollo.&lt;/p&gt;
&lt;h3 id=&#34;prisma&#34;&gt;Prisma&lt;/h3&gt;
&lt;p&gt;Prisma is a cool library developed by the same people as Graph.cool that does much of the work for you in enabling GraphQL access to your database.&lt;/p&gt;
&lt;p&gt;Prisma offers configuration for existing databases, but unfortunately I had trouble getting it to work on my Ubuntu system—I ran into issues getting the Docker container to connect to my local Postgres and MongoDB databases. However, following the quick guide found &lt;a href=&#34;https://www.prisma.io/docs/get-started/01-setting-up-prisma-new-database-JAVASCRIPT-a002/&#34;&gt;here&lt;/a&gt; on the Prisma website, I was able to get a GraphQL server up and running inside a Docker container with a new database. The process was simple:&lt;/p&gt;
&lt;p&gt;First, you have to install the Prisma command line utility:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g prisma&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You also need to have Docker installed. Documentation for Docker can be found &lt;a href=&#34;https://docs.docker.com/get-started/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, you need to configure Prisma. Create a directory for your Prisma server, and create a new file named &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir hello-world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd hello-world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;touch docker-compose.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, paste the following into it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;version: &amp;#39;3&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  prisma:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image: prismagraphql/prisma:1.34
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    restart: always
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &amp;#39;4466:4466&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      PRISMA_CONFIG: |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        port: 4466
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        databases:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          default:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            connector: mongo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            uri: mongodb://prisma:prisma@mongo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mongo:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    image: mongo:3.6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    restart: always
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      MONGO_INITDB_ROOT_USERNAME: prisma
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      MONGO_INITDB_ROOT_PASSWORD: prisma
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &amp;#39;27017:27017&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - mongo:/var/lib/mongo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mongo: ~&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I used Mongo here, but Prisma’s site has guides for PostgreSQL and MySQL as well. It’s important to make sure now that you don’t have any conflicts with currently running databases—on my machine I had already had a MongoDB server running on port 27017. I fixed this by just stopping my local MongoDB server, but I’m sure you could configure the Docker containers to work with different ports as well. Running Ubuntu, I just ran &lt;code&gt;sudo service mongodb stop&lt;/code&gt; and then the Prisma Docker containers worked just fine. When I was done, I ran &lt;code&gt;sudo service mongodb start&lt;/code&gt; to start it up again.&lt;/p&gt;
&lt;p&gt;Next, you’ll start the Prisma containers and initialize the Prisma server configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;prisma init --endpoint http://localhost:4466&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The final step is to deploy the service:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;prisma deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all goes well, you’ll see a message that includes a URL to the Prisma Admin, which is a browser tool to interact with your GraphQL endpoints. I used it for a little while when I was testing Prisma out, and it seems to work well and is easy to use.&lt;/p&gt;
&lt;p&gt;All in all, Prisma seems like a great way to start if you don’t want to handle the messy details of setup. However, I did have issues getting it to play nice with my already-existing databases (including both PostgreSQL and MongoDB). However, it is still relatively new, so I would expect support to get better over time.&lt;/p&gt;
&lt;h3 id=&#34;graphene-python&#34;&gt;Graphene (Python)&lt;/h3&gt;
&lt;p&gt;Graphene is a GraphQL framework for Python. It has integrations for a few different server frameworks (a list can be found &lt;a href=&#34;https://github.com/graphql-python/graphene&#34;&gt;here&lt;/a&gt;), but I’ll show examples from &lt;code&gt;graphene-django&lt;/code&gt;, since Django is fairly common and something that we use fairly often at End Point.&lt;/p&gt;
&lt;p&gt;Because you’re also setting up a Django project, the tutorial for graphene-django is a little more involved, so I’ll just share the relevant GraphQL sections so you can compare to the other libraries in this post. The most important part, the schema, is defined in Python with a similar format to Django models:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import graphene
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from graphene_django.types import DjangoObjectType
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from app.models import Category, Ingredient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class CategoryType(DjangoObjectType):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    class Meta:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model = Category
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class IngredientType(DjangoObjectType):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    class Meta:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model = Ingredient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class Query(object):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_categories = graphene.List(CategoryType)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    all_ingredients = graphene.List(IngredientType)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    def resolve_all_categories(self, info, **kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return Category.objects.all()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    def resolve_all_ingredients(self, info, **kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        # We can easily optimize query count in the resolve method
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return Ingredient.objects.select_related(&amp;#39;category&amp;#39;).all()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the format for defining your GraphQL schema is quite different from some other libraries, but you have the advantage of it looking similar to Django’s model definitions. You’ll also need a higher-level Query definition:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import graphene
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import cookbook.ingredients.schema
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;class Query(cookbook.ingredients.schema.Query, graphene.ObjectType):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # This class will inherit from multiple Queries
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # as we begin to add more apps to our project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pass
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema = graphene.Schema(query=Query)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that we have a schema defined, we need to add a few things to &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # This will also make the `graphql_schema` management command available
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#39;graphene_django&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GRAPHENE = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#39;SCHEMA&amp;#39;: &amp;#39;cookbook.schema.schema&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last piece needed to use your GraphQL schema is in &lt;code&gt;urls.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;from graphene_django.views import GraphQLView
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url(r&amp;#39;^admin/&amp;#39;, admin.site.urls),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url(r&amp;#39;^graphql$&amp;#39;, GraphQLView.as_view(graphiql=True)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally we run the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ python manage.py runserver&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you should be able to use your GraphQL schema at http://localhost:8000/graphql just like with any other GraphQL server.&lt;/p&gt;
&lt;p&gt;Graphene for Django seems like a good solution in that it uses a similar format to other aspects of Django, like the model definitions. However, its format (especially for schema definition) is rather different-looking from the GraphQL standard used by most other libraries, and it seems like it might make it more work to keep your frontend and backend in sync.&lt;/p&gt;
&lt;h3 id=&#34;graphcool&#34;&gt;Graph.cool&lt;/h3&gt;
&lt;p&gt;I won’t discuss Graph.cool in detail here, because I went over it in my previous blog post. However, it still merits mention here as an option for your GraphQL server. Essentially, Graph.cool lets you define a GraphQL schema and then handles the work of setting up a database and even hosting for you. If you just want to get a basic GraphQL server set up for testing, or if you don’t need too many features beyond data storage and retrieval, Graph.cool is a great choice.&lt;/p&gt;
&lt;h3 id=&#34;additional-links&#34;&gt;Additional links&lt;/h3&gt;
&lt;p&gt;For server libraries in other languages, these seem like good options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://graphql-ruby.org/&#34;&gt;GraphQL Ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.graphql-java.com/&#34;&gt;GraphQL Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/graph-gophers/graphql-go&#34;&gt;graphql-go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading! Keep an eye out next week for a second post which will cover GraphQL client libraries.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Client Case Study: Carjojo</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/12/client-case-study-carjojo/"/>
      <id>https://www.endpointdev.com/blog/2016/12/client-case-study-carjojo/</id>
      <published>2016-12-23T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;https://web.archive.org/web/20161221010712/https://www.carjojo.com/&#34;&gt;Carjojo&lt;/a&gt;’s site makes use of some of the best tools on the market today for accessing and displaying data. Carjojo is a car buying application that takes data about car pricing, dealer incentives, and rebate programs and aggregates that into a location-specific vehicle pricing search tool. The Carjojo work presented a great opportunity for End Point to utilize our technical skills to build a state-of-the-art application everyone is very proud of. End Point worked on the Carjojo development project from October of 2014 through early 2016, and the final Carjojo application launched in the summer of 2016. This case study shows that End Point can be a technology partner for a startup, enabling the client to maintain their own business once our part of the project is over.&lt;/p&gt;
&lt;h3 id=&#34;why-end-point&#34;&gt;Why End Point?&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float: right; text-align: center;&#34;&gt;&lt;img height=&#34;375&#34; id=&#34;docs-internal-guid-bf89d7d8-193d-dd5c-ed85-19fbe48e18e4&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-0.png&#34; style=&#34;border: medium none; transform: rotate(0rad);&#34; width=&#34;338&#34;/&gt;&lt;/div&gt;
&lt;h4 id=&#34;reputation-in-full-stack-development&#34;&gt;Reputation in full stack development&lt;/h4&gt;
&lt;p&gt;End Point has deep experience with full stack development so for a startup getting advice from our team can prove really helpful when deciding what technologies to implement and what timelines are realistic. Even though the bulk of the Carjojo work focused on specific development pieces, having developers available to help advise on the entire stack allows a small startup to leverage a much broader set of skills.&lt;/p&gt;
&lt;h4 id=&#34;startup-budget-and-timelines&#34;&gt;Startup Budget and Timelines&lt;/h4&gt;
&lt;p&gt;End Point has worked with a number of startups throughout our time in the business. Startups require particular focused attention on budget and timelines to ensure that the minimum viable product can be ready on time and that the project stays on budget. Our consultants focus on communication with the client and advise them on how to steer the development to meet their needs, even if those shift as the project unfolds.&lt;/p&gt;
&lt;h4 id=&#34;client-side-development-team&#34;&gt;Client Side Development Team&lt;/h4&gt;
&lt;p&gt;One of the best things about a lot of our clients is their technological knowledge and the team they bring to the table. In the case of Carjojo, End Point developers fit inside of their Carjojo team to build parts that they were unfamiliar with. End Point developers are easy to work with and already work in a remote development environment, so working in a remote team is a natural fit.&lt;/p&gt;
&lt;h4 id=&#34;client-side-project-management&#34;&gt;Client Side Project Management&lt;/h4&gt;
&lt;p&gt;End Point works on projects where either the project management is done in-house or by the client. In the case of a project like Carjojo where the client has technical project management resources, our engineers work within that team. This allows a startup like Carjojo insight into the project on a daily basis.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float: left; text-align: center;&#34;&gt;&lt;img height=&#34;350&#34; id=&#34;docs-internal-guid-adf4efb0-193f-f2b6-279f-301698d2fdc2&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-1.png&#34; style=&#34;-webkit-transform: rotate(0.00rad); border: none; transform: rotate(0.00rad);&#34; width=&#34;150&#34;/&gt;&lt;/div&gt;
&lt;h3 id=&#34;project-overview&#34;&gt;Project Overview&lt;/h3&gt;
&lt;p&gt;The main goal of the Carjojo project was to aggregate several data sources on car price and use data analytics to provide useful shopper information, and display that for their clients.&lt;/p&gt;
&lt;p&gt;Carjojo’s staff had experience in the car industry and leveraged that to build a sizeable database of information. Analytics work on the database provided another layer of information, creating a time- and location-specific market value for a vehicle.&lt;/p&gt;
&lt;p&gt;Carjojo kept the bulk of the database collection and admin work in house, as well as provided an in-house designer that closely worked with them on their vision for the project. End Point partnered to do the API architecture work as well as the front end development.&lt;/p&gt;
&lt;p&gt;A major component of this project was using a custom API to pull information from the database and display it quickly with high end, helpful infographics. Carjojo opted to use APIs so that the coding work would seamlessly integrate with future plans for a mobile application, which normally require a substantial amount of recoding.&lt;/p&gt;
&lt;p&gt;Creating a custom API also allows Carjojo to work with future partners and leverage their data and analytics in new ways as their business grows.&lt;/p&gt;
&lt;h3 id=&#34;team&#34;&gt;Team&lt;/h3&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float: left; text-align: center;&#34;&gt;&lt;img height=&#34;151&#34; id=&#34;docs-internal-guid-c5221843-1937-962a-43e0-fc2b316dbed6&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-2.png&#34; style=&#34;border: medium none; transform: rotate(0rad);&#34; width=&#34;151&#34;/&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Patrick Lewis&lt;/strong&gt;: End Point project manager and front end developer. Patrick led development of the AngularJS front end application which serves as the main customer car shopping experience on the Carjojo site. He also created data stories using combinations of integrated Google Maps, D3/DimpleJS charts, and data tables to aid buyers with car searches and comparisons.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float: left; text-align: center;&#34;&gt;&lt;img height=&#34;153&#34; id=&#34;docs-internal-guid-adf4efb0-1938-fb69-fe18-cada954bb90b&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-3.png&#34; style=&#34;border: medium none; transform: rotate(0rad);&#34; width=&#34;153&#34;/&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Matt Galvin:&lt;/strong&gt; Front end developer. Matt led the efforts for data-visualization with D3 and DimpleJS. He created Angular services that were used to communicate with the backend, used D3 and DimpleJS to illustrate information graphically about cars, car dealers, incentives, etc., sometimes neatly packaging them into directives for easy re-use when the case fit. He also created a wealth of customizations and extensions of DimpleJS which allowed for rapid development without sacrificing visualization quality.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float: left; text-align: center;&#34;&gt;&lt;b&gt;&lt;img height=&#34;155&#34; id=&#34;docs-internal-guid-adf4efb0-1939-cb73-eb4e-29fafe866ac4&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-4.png&#34; style=&#34;-webkit-transform: rotate(0.00rad); border: none; transform: rotate(0.00rad);&#34; width=&#34;155&#34;/&gt;&lt;/b&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Josh Williams:&lt;/strong&gt; Python API development. Josh led the efforts in connecting the database into Django and Python to process and aggregate the data as needed. He also used TastyPie to format the API response and created authentication structures for the API.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; float: right; text-align: center;&#34;&gt;&lt;img height=&#34;279&#34; id=&#34;docs-internal-guid-adf4efb0-1941-c6b8-7c03-547234b8105c&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-5.png&#34; style=&#34;border: medium none; transform: rotate(0rad);&#34; width=&#34;309&#34;/&gt; &lt;/div&gt;
&lt;h3 id=&#34;project-specifics&#34;&gt;Project Specifics&lt;/h3&gt;
&lt;h4 id=&#34;api-tools&#34;&gt;API Tools&lt;/h4&gt;
&lt;p&gt;Carjojo’s project makes use of some of the best tools on the market today for accessing and displaying data. &lt;a href=&#34;https://www.djangoproject.com/&#34;&gt;Django&lt;/a&gt; and &lt;a href=&#34;http://tastypieapi.org/&#34;&gt;Tastypie&lt;/a&gt; were chosen to allow for rapid API development and to keep the response time down on the website. In most cases the Django ORM was sufficient for generating queries from the data, though in some cases custom queries were written to better aggregate and filter the data directly within Postgres.&lt;/p&gt;
&lt;p&gt;To use the location information in the database, some GIS location smarts were tied into Tastypie. Location searches tied into &lt;a href=&#34;https://docs.djangoproject.com/en/1.10/ref/contrib/gis/&#34;&gt;GeoDjango&lt;/a&gt; and generated &lt;a href=&#34;http://postgis.net/&#34;&gt;PostGIS&lt;/a&gt; queries in the database.&lt;/p&gt;
&lt;h4 id=&#34;front-end-tools&#34;&gt;Front End Tools&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://d3js.org/&#34;&gt;D3&lt;/a&gt; is standard in data-visualization and is great for doing both simple and complicated graphics. Many of Carjojo’s graphs were bar graphs, pie charts and didn’t really require writing out D3 by hand. We also wanted to make many of them reusable and dynamic (often based on search terms or inputs) with use of Angular directives and services. This could have been done with pure D3, but Dimple makes creating simple D3 graphs easy and fast.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://dimplejs.org/&#34;&gt;DimpleJS&lt;/a&gt; was used a lot in this project. Since Carjojo is data-driven, they wanted to display their information in an aesthetically pleasing manner and DimpleJS allowed us to quickly spin up information against some of the project’s tightest deadlines.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;span id=&#34;docs-internal-guid-adf4efb0-192c-c5d7-47f8-c173ae3b292c&#34; style=&#39;background-color: transparent; color: black; font-family: &#34;arial&#34;; font-size: 14.6667px; font-style: normal; font-variant: normal; font-weight: 400; margin-left: 1em; margin-right: 1em; text-decoration: none; vertical-align: baseline;&#39;&gt;&lt;img height=&#34;120&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-6.png&#34; style=&#34;border: medium none; transform: rotate(0rad);&#34; width=&#34;356&#34;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;The approach worked well for most cases. However, sometimes Carjojo wanted something slightly different than what DimpleJS does out of the box. One example of DimpleJS customization work can be found &lt;a href=&#34;/blog/2015/10/intro-to-dimplejs-graphing-in-6-easy/&#34;&gt;here on our blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another thing to note about the data visualizations was that sometimes when the data was plotted and graphed, it brought to light some discrepancies in the back-end calculations and analytics, requiring some back-and-forth between the Carjojo DBA and End Point.&lt;/p&gt;
&lt;h3 id=&#34;results&#34;&gt;Results&lt;/h3&gt;
&lt;p&gt;Carjojo had a successful launch of their service in the summer of 2016. Their system has robust user capabilities, a modern clean design, and a solid platform to grow from. The best news for Carjojo is that now the project has been turned back over to them for development. End Point believes in empowering our clients to move forward with their business and goals without us. Carjojo knows that we’ll be here for support if they need it.&lt;/p&gt;
&lt;img height=&#34;491&#34; id=&#34;docs-internal-guid-adf4efb0-192c-4e5e-97ff-690dabfbb0ec&#34; src=&#34;/blog/2016/12/client-case-study-carjojo/image-7.png&#34; style=&#34;border: medium none; transform: rotate(0rad);&#34; width=&#34;640&#34;/&gt;

      </content>
    </entry>
  
    <entry>
      <title>Executing Custom SQL in Django Migrations</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/09/executing-custom-sql-in-django-migration/"/>
      <id>https://www.endpointdev.com/blog/2016/09/executing-custom-sql-in-django-migration/</id>
      <published>2016-09-17T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;Since version 1.7, Django has natively supported database migrations similar to Rails migrations. The biggest difference fundamentally between the two is the way the migrations are created: Rails migrations are written by hand, specifying changes you want made to the database, while Django migrations are usually automatically generated to mirror the database schema in its current state.&lt;/p&gt;
&lt;p&gt;Usually, Django’s automatic schema detection works quite nicely, but occasionally you will have to write some custom migration that Django can’t properly generate, such as a functional index in PostgreSQL.&lt;/p&gt;
&lt;h3 id=&#34;creating-an-empty-migration&#34;&gt;Creating an empty migration&lt;/h3&gt;
&lt;p&gt;To create a custom migration, it’s easiest to start by generating an empty migration. In this example, it’ll be for an application called blog:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./manage.py makemigrations blog --empty -n create_custom_index
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Migrations &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  0002_create_custom_index.py:&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This generates a file at blog/migrations/0002_create_custom_index.py that will look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Generated by Django 1.9.4 on 2016-09-17 17:35&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;__future__&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; unicode_literals
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; migrations
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Migration&lt;/span&gt;(migrations.Migration):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dependencies = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    operations = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;adding-custom-sql-to-a-migration&#34;&gt;Adding Custom SQL to a Migration&lt;/h3&gt;
&lt;p&gt;The best way to run custom SQL in a migration is through the &lt;a href=&#34;https://docs.djangoproject.com/en/1.10/ref/migration-operations/#runsql&#34;&gt;migration.RunSQL&lt;/a&gt; operation. RunSQL allows you to write code for migrating forwards and backwards—​that is, applying migrations and unapplying them. In this example, the first string in RunSQL is the forward SQL, the second is the reverse SQL.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# Generated by Django 1.9.4 on 2016-09-17 17:35&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;__future__&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; unicode_literals
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; migrations
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Migration&lt;/span&gt;(migrations.Migration):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dependencies = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;0001_initial&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    operations = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        migrations.RunSQL(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;CREATE INDEX i_active_posts ON posts(id) WHERE active&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DROP INDEX i_active_posts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unless you’re using Postgres for your database, you’ll need to install the &lt;a href=&#34;https://pypi.python.org/pypi/sqlparse&#34;&gt;sqlparse&lt;/a&gt; library, which allows Django to break the SQL strings into individual statements.&lt;/p&gt;
&lt;h3 id=&#34;running-the-migrations&#34;&gt;Running the Migrations&lt;/h3&gt;
&lt;p&gt;Running your migrations is easy:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ./manage.py migrate
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Operations to perform:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Apply all migrations: blog, sessions, auth, contenttypes, admin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running migrations:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Rendering model states... DONE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Applying blog.0002_create_custom_index... OK&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unapplying migrations is also simple. Just provide the name of the app to migrate and the id of the migration you want to go to, or “zero” to reverse all migrations on that app:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$./manage.py migrate blog &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Operations to perform:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Target specific migration: 0001_initial, from blog
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running migrations:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Rendering model states... DONE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Unapplying blog.0002_create_custom_index... OK&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Hand-written migrations can be used for many other operations, including data migrations. Full documentation for migrations can be found in the &lt;a href=&#34;https://docs.djangoproject.com/en/1.10/topics/migrations/&#34;&gt;Django documentation&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;(This post originally covered South migrations and was updated by &lt;a href=&#34;/blog/authors/phin-jensen/&#34;&gt;Phin Jensen&lt;/a&gt; to illustrate the now-native Django migrations.)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Book review: “Two Scoops of Django: Best Practices for Django 1.8”</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/07/book-review-two-scoops-of-django-best/"/>
      <id>https://www.endpointdev.com/blog/2016/07/book-review-two-scoops-of-django-best/</id>
      <published>2016-07-13T00:00:00+00:00</published>
      <author>
        <name>Phineas Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;em&gt;Two Scoops of Django: Best Practices for Django 1.8&lt;/em&gt; is, shockingly, a book about best practices. It’s not a Django library reference, or a book about Django fundamentals, or tips and tricks. It’s a book designed to help web developers, novices and experts alike, avoid common pitfalls in every stage of the web development process, specifically the process of developing with Django.&lt;/p&gt;
&lt;p&gt;The book can be used as a reference of best practices and a cover-to-cover guide to best practices. I’ve done both and found it to be enjoyable, accessible, and educational when read cover-to-cover and a valuable reference when setting up a new Django project or doing general Django development. It covers a huge range of material, answering questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where should I store secret keys?&lt;/li&gt;
&lt;li&gt;Should I use virtualenv?&lt;/li&gt;
&lt;li&gt;How should I structure my Django project?&lt;/li&gt;
&lt;li&gt;When should I use ‘blank’ and ‘null’ in model fields?&lt;/li&gt;
&lt;li&gt;Should I use Class-Based Views or Function-Based Views?&lt;/li&gt;
&lt;li&gt;How should I structure my URLConfs?&lt;/li&gt;
&lt;li&gt;When should I use Forms?&lt;/li&gt;
&lt;li&gt;Where should templates be stored?&lt;/li&gt;
&lt;li&gt;Should I write custom template tags and filters?&lt;/li&gt;
&lt;li&gt;What package should I use to create a REST API?&lt;/li&gt;
&lt;li&gt;What core components should I replace?&lt;/li&gt;
&lt;li&gt;How can I modify or replace the User and authentication system?&lt;/li&gt;
&lt;li&gt;How should I test my app?&lt;/li&gt;
&lt;li&gt;How can I improve performance?&lt;/li&gt;
&lt;li&gt;How can I keep my app secure?&lt;/li&gt;
&lt;li&gt;How do I properly use the logging library?&lt;/li&gt;
&lt;li&gt;How do I deploy to a Platform as a Service or my own server(s)?&lt;/li&gt;
&lt;li&gt;What can I do to improve the debugging process?&lt;/li&gt;
&lt;li&gt;What are some good third-party packages?&lt;/li&gt;
&lt;li&gt;Where can I find good documentation and tutorials?&lt;/li&gt;
&lt;li&gt;Where should I go to ask more questions?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The question, then, is whether or not this book delivers this information well. For the most part, it does. It’s important to recognize that the book doesn’t cover any of these subjects in great detail, but it does do a great job explaining the “why” behind some of the simple rules that are established and referencing online resources that can be used to go much more in-depth with the subject. It does a great job showing clearly marked bad examples, making it very easy to see whether or not you are or were planning on doing something badly. The writing style is very accessible and straightforward; I read large portions of the book during breakfast or lunch.&lt;/p&gt;
&lt;p&gt;Two sections stood out to me as being very helpful for my own projects. First is Chapter 4, Fundamentals of Django App Design, which explained better than any resource I’ve found yet exactly what a Django “app” (as in ./manage.py startapp polls) should be used for. It explains what an app should or shouldn’t have in it, how much an app should do, when you should break into separate apps, and more.&lt;/p&gt;
&lt;p&gt;The next section that really helped me was Chapter 6, Model Best Practices, which explained things like what code should and shouldn’t be in a model, how to use migrations and managers, and ModelFields that should be avoided. Perhaps the most useful part of that chapter is a table in the section “When to use Null and Blank,” which makes for an easy and quick reference to which fields go well with the null and blank parameters and when you should use both or neither.&lt;/p&gt;
&lt;p&gt;The only real problem I had with Two Scoops of Django was that the illustrations rarely felt necessary or helpful. The majority of them are ice cream-themed jokes that aren’t particularly funny. Overall, I really enjoyed this book and I definitely recommend it for anybody who is or is interested in doing serious Django development.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Testing Django Applications</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/12/testing-django-applications/"/>
      <id>https://www.endpointdev.com/blog/2015/12/testing-django-applications/</id>
      <published>2015-12-14T00:00:00+00:00</published>
      <author>
        <name>Zdeněk Maxa</name>
      </author>
      <content type="html">
        &lt;p&gt;This post summarizes some observations and guidelines originating from introducing the &lt;a href=&#34;http://pytest.org/latest/&#34;&gt;pytest&lt;/a&gt; unit testing framework into our CMS (Content Management System) component of the &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy&lt;/a&gt;.
Our Django-based CMS allows users to define scenes, presentations and assets (StreetView, Earth tours, panos, etc) to be displayed on the
&lt;a href=&#34;https://www.youtube.com/watch?v=2VonXkA6YYg&#34;&gt;Liquid Galaxy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The purpose of this blog post is to capture my &lt;a href=&#34;https://www.djangoproject.com/&#34;&gt;Django&lt;/a&gt; and testing study points, summarize useful resource links as well as to itemize some guidelines for implementing tests for newcomers to the project. It also provides a comparison between Python’s standard &lt;a href=&#34;https://docs.python.org/2/library/unittest.html&#34;&gt;unittest library&lt;/a&gt; and the aforementioned pytest. Its focus is on Django database interaction.&lt;/p&gt;
&lt;h3 id=&#34;versions-of-software-packages-used&#34;&gt;Versions of software packages used&lt;/h3&gt;
&lt;p&gt;This post describes some of our experiences at End Point in designing and working on comprehensive QA/CI facilities for a new system which is closely related to the Liquid Galaxy.&lt;/p&gt;
&lt;p&gt;The experiments were done on Ubuntu Linux 14.04:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;python (2.7.6) and its corresponding version of unittest&lt;/li&gt;
&lt;li&gt;django 1.7 (current recent is 1.9 but our CMS uses still 1.7 version)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pytest-django.readthedocs.org/en/latest/&#34;&gt;pytest-django 2.8.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;pytest 2.7.2 (with py 1.4.30)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://pypi.python.org/pypi/virtualenv&#34;&gt;virtualenv 13.1.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://factoryboy.readthedocs.org/en/latest/&#34;&gt;factory_boy 2.6.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;testing-django-applications&#34;&gt;Testing Django Applications&lt;/h3&gt;
&lt;p&gt;We probably don’t need to talk much about the importance of testing. Writing tests along with the application code has become standard over the years. Surely, developers may fall into a trap of their own prejudice when creating testing conditions which would still result in faulty software but the likelihood of buggy software is certainly higher on a code that has no QA measures. If the code works and is untested, it means it works by accident, they say.&lt;/p&gt;
&lt;p&gt;As a rule of thumb, unit tests should be very brief testing items seldom interacting with any external services such as the database. Integration tests on the other hand often communicate with external components.&lt;/p&gt;
&lt;p&gt;This post will heavily reference an example minimal Django application written for the purpose of experimenting on
&lt;a href=&#34;https://github.com/zdenekmaxa/examples/tree/master/python/django-testing&#34;&gt;Django testing&lt;/a&gt;.
Its README file contains some set up and requirement notes. Also, I am not going to list (m)any code snippets here but rather reference the functional application and its test suite. Hence the points below qualify for more or less assorted little topics or observations.&lt;/p&gt;
&lt;p&gt;In order to benefit from this post, it will be helpful to follow the README and interact (run tests that is) with the demo django-testing application.&lt;/p&gt;
&lt;h3 id=&#34;basic-django-unittest-versus-pytest-basic-examples&#34;&gt;Basic Django unittest versus pytest basic examples&lt;/h3&gt;
&lt;p&gt;This pair of test modules shows the differences between Django TestCase (unittest) and pytest-django (pytest) frameworks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/zdenekmaxa/examples/blob/master/python/django-testing/tests/test_unittest_style.py&#34;&gt;test_unittest_style.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The base Django TestCase class derives along this tree:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    django.test.TestCase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        django.test.TransactionTestCase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            django.test.SimpleTestCase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                unittest.TestCase&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Django adds (among any other aspects) handling of database, the documentation is
&lt;a href=&#34;https://docs.djangoproject.com/en/1.7/topics/testing/overview/&#34;&gt;here&lt;/a&gt;, on top of the Python standard unittest library.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/zdenekmaxa/examples/blob/master/python/django-testing/tests/test_pytest_style.py&#34;&gt;test_pytest_style.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;this is a pytest style implementation of the same tests and pytest-django plug-in adds, among other features, Django database handling support.&lt;/p&gt;
&lt;p&gt;The advantage of unittest is that it comes with the Python installation—​it’s a standard library. That means that one does not have to install anything for writing tests, unlike pytest which is a third-party library and needs to be installed separately. While the absence of additional installation is certainly a plus, it’s dubious whether being a part of Python distribution is a benefit. I seem to recall Guido Van Rossum during Europython 2010 having said the the best thing for pytest is not being part of the Python standard set of libraries for its lively development and evolution would be slowed down by the inclusion.&lt;/p&gt;
&lt;p&gt;There are very good talks and articles summarizing advantages of pytest. For me personally, the reporting of error
context is supreme. No boiler-plate (no inheritance), using plain Python asserts instead of
many &lt;a href=&#34;https://docs.python.org/2/library/unittest.html#assert-methods&#34;&gt;assert* methods&lt;/a&gt; and
flexibility (function, class) are other big plus points&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://speakerdeck.com/pelme/testing-django-applications-with-py-dot-test-europython-2013&#34;&gt;Testing Django applications with py.test (EuroPython 2013)&lt;/a&gt; (very good)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://prezi.com/knnfwewm7lvw/pytest-rapid-multi-purpose-testing/&#34;&gt;pytest presentation from its author&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.pydanny.com/pytest-no-boilerplate-testing.html&#34;&gt;very brief pytest introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://mathieu.agopian.info/presentations/2015_06_djangocon_europe/&#34;&gt;switch to pytest&lt;/a&gt;, rich features descriptions, has some Django touches&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As the comment in the &lt;strong&gt;test_unittest_style.py&lt;/strong&gt; file says, this particular unittest-based test module can be run by both
Django &lt;strong&gt;manage.py&lt;/strong&gt; (which boils down to unittest lookup discovery on a lower layer) or by &lt;strong&gt;py.test&lt;/strong&gt; (pytest).&lt;/p&gt;
&lt;p&gt;It should also be noted, that pytest’s flexibility can &lt;a href=&#34;http://stackoverflow.com/questions/21430900/py-test-skips-test-class-if-constructor-is-defined&#34;&gt;bite back&lt;/a&gt; if something gets overlooked.&lt;/p&gt;
&lt;h3 id=&#34;django-database-interaction-unittest-versus-pytest-advanced-examples&#34;&gt;Django database interaction unittest versus pytest (advanced examples)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/zdenekmaxa/examples/blob/master/python/django-testing/tests/test_unittest_advanced.py&#34;&gt;test_unittest_advanced.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since this post concentrates on pytest and since it’s the choice for our LG CMS project
(naturally :-), this unittest example just shows how the
&lt;strong&gt;test (fresh) database&lt;/strong&gt; is determined and how Django migrations are run at each test suite execution. Just as described in
the &lt;a href=&#34;https://docs.djangoproject.com/en/1.7/topics/testing/overview/&#34;&gt;Django documentation&lt;/a&gt;: “If your tests rely on database access such as creating or querying models, be sure to create your test classes as subclasses of django.test.TestCase rather than unittest.TestCase.”&lt;/p&gt;
&lt;p&gt;That is true for database interaction but not completely true when using pytest. And “Using unittest.TestCase avoids the cost of running each test in a transaction and flushing the database, but if your tests interact with the database their behavior will vary based on the order that the test runner executes them. This can lead to unit tests that pass when run in isolation but fail when run in a suite.”
&lt;strong&gt;django.test.TestCase&lt;/strong&gt;, however, ensures that each test runs inside a transaction to provide isolation.
The transaction is rolled back once the test case is over.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/zdenekmaxa/examples/blob/master/python/django-testing/tests/test_pytest_advanced.py&#34;&gt;test_pytest_advanced.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This file represents the actual core of the test experiments for this blog / demo app and shows various pytest features and approaches typical for this framework as well as Django (pytest-django that is) specifics.&lt;/p&gt;
&lt;h3 id=&#34;django-pytest-notes-advanced-example&#34;&gt;Django pytest notes (advanced example)&lt;/h3&gt;
&lt;p&gt;Much like the unittest documentation, the &lt;a href=&#34;https://pytest-django.readthedocs.org/en/latest/&#34;&gt;pytest-django&lt;/a&gt; recommends avoiding database interaction in unittest and concentrate only on the logic which should be designed in such a fashion that it can be tested without database.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;test database name prefixed &lt;code&gt;test_&lt;/code&gt; (just like at the unittest example), the base value is taken from the database section of
the &lt;strong&gt;settings.py&lt;/strong&gt;. As a matter of fact, it’s possible to run the test suite after previously dropping the main database,
the test suite interacts only with &lt;code&gt;test_&lt;/code&gt; + DATABASE_NAME&lt;/li&gt;
&lt;li&gt;migration execution before any database interaction is carried out (similarly to unittest example)&lt;/li&gt;
&lt;li&gt;database interaction marked by a Python decorator &lt;strong&gt;@pytest.mark.django_db&lt;/strong&gt; on the method or class level (or stand-alone function level). It’s in fact the &lt;strong&gt;first&lt;/strong&gt; occurrence of this marker which triggers the database set up (its creation and migrations handling). Again analogously to unittest (django.test.TestCase), the test case is wrapped in a database transaction which puts the database back into the state prior to the test case. The database &lt;code&gt;test_&lt;/code&gt; + DATABASE_NAME itself is dropped once the test suite run is over. The database is not dropped if &lt;code&gt;--db-reuse&lt;/code&gt; option is used. The production DATABASE_NAME remains untouched during the test suite run (more about this below)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pytest_djangodb_only.py — setup_method&lt;/strong&gt; — run this module separately and the data created in setup_method end up &lt;strong&gt;NOT&lt;/strong&gt; in the &lt;code&gt;test_&lt;/code&gt; + DATABASE_NAME database but in the standard one (as configured in the &lt;strong&gt;settings.py&lt;/strong&gt; which would be the production database likely)! Also this data won’t be rolled back. When run separately, this test module will pass (but still the production database would be tainted). It may or may not fail on the second and subsequent run depending whether it creates any unique data. When run within the test suite, the database call from the setup_method will fail despite the presence of the class django_db marker. This has been very important to realize.
Recommendation: do not include database interaction in the pytest special methods
(such assetup_method or teardown_method, etc), &lt;strong&gt;only include database interaction in the test case methods&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The error message &lt;code&gt;Failed: Database access not allowed, use the &amp;quot;django_db&amp;quot; mark to enable&lt;/code&gt; was seen on a database error on a method which actually had the marker. This output is not to be 100% trusted&lt;/li&gt;
&lt;li&gt;data model &lt;strong&gt;factories&lt;/strong&gt; are discussed separately below&lt;/li&gt;
&lt;li&gt;lastly the test module shows Django Client instance and calling an HTTP resource&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;pytest-setup_method&#34;&gt;pytest setup_method&lt;/h3&gt;
&lt;p&gt;While the fundamental differences between unittest and pytest were discussed, there is something to be said about Django
specific differences of the two. There is different database-related behaviour of
&lt;strong&gt;unittest setUp method&lt;/strong&gt; versus the &lt;strong&gt;pytest setup_method method&lt;/strong&gt;. The setUp is included in the transaction and database interactions are rolled back once the test case is over. The setup_method is not included in the transaction. Moreover, interacting with the database from setup_method results in faulty behaviour and difference depending whether the test module is run on its own or as a part of the whole test suite.&lt;/p&gt;
&lt;p&gt;The bottom line is: do not include database interaction in setup_method. This setUp, setup_method behaviour was already shown in the basic examples. And more description and demonstration of this behaviour is in the file: &lt;strong&gt;pytest_djangodb_only.py&lt;/strong&gt;. This actually revealed the fact that using django_db database fixture is not supported in special pytest methods and the aforementioned error message is misleading (more references &lt;a href=&#34;https://github.com/pytest-dev/pytest-django/issues/297&#34;&gt;here&lt;/a&gt; and
&lt;a href=&#34;http://stackoverflow.com/questions/34089425/django-pytest-setup-method-database-issue&#34;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;When running the whole test suite, this file won’t be collected (its name lacks &lt;code&gt;test_&lt;/code&gt; string).
It needs to be renamed to be included in the test suite run.&lt;/p&gt;
&lt;h3 id=&#34;json-data-fixtures-versus-factories-pytest-advanced-example&#34;&gt;JSON data fixtures versus factories (pytest advanced example)&lt;/h3&gt;
&lt;p&gt;The traditional way of interacting with some test data was to perform following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;have data loaded in the database&lt;/li&gt;
&lt;li&gt;python manage.py dumpdata&lt;/li&gt;
&lt;li&gt;the produced JSON file is dragged along the application test code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;call_command(&amp;ldquo;loaddata&amp;rdquo;, fixture_json_file_name)&lt;/strong&gt; happens at each test suite run&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The load is expensive, the JSON dump file is hard to maintain manually if the original modified copy and the current needs diverge (the file has integer primary keys value, etc). Although even the recent Django testing documentation mentions usage of JSON data fixtures, the approach is considered discouraged and the goal is recommended to achieve by means of loading the data in migrations or using model data factories.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.caktusgroup.com/blog/2013/07/17/factory-boy-alternative-django-testing-fixtures/&#34;&gt;This talk&lt;/a&gt; for example compares the both approaches in favour of &lt;a href=&#34;https://factoryboy.readthedocs.org/en/latest/&#34;&gt;factory_boy library&lt;/a&gt;.
A quote from the article:
“Factory Boy is a Python port of a popular Ruby project called Factory Girl. It provides a declarative syntax for how new instances should be created. &amp;hellip; Using fixtures for complex data structures in your tests is fraught with peril. They are hard to maintain and they make your
tests slow. Creating model instances as they are needed is a cleaner way to write your tests which will make them faster and more maintainable.”&lt;/p&gt;
&lt;p&gt;The file &lt;strong&gt;test_pytest_advanced.py&lt;/strong&gt; demostrates interaction with factories defined in the module
&lt;strong&gt;factories.py&lt;/strong&gt;, the basic very easy-to-use features.&lt;/p&gt;
&lt;p&gt;Despite its ease of use, the factory_boy is a powerful library capable of modeling
&lt;a href=&#34;http://factoryboy.readthedocs.org/en/latest/recipes.html#many-to-many-relation-with-a-through&#34;&gt;Django’s ORM many-to-many relationships&lt;/a&gt;, among other features.&lt;/p&gt;
&lt;h3 id=&#34;additional-useful-links&#34;&gt;Additional useful links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.djangoproject.com/en/1.7/topics/testing/overview/&#34;&gt;Django 1.7 testing&lt;/a&gt; — version used in the demo application&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.djangoproject.com/en/2.0/topics/testing/overview/&#34;&gt;Django 2.0 testing&lt;/a&gt; — latest stable version&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.effectivedjango.com/&#34;&gt;Effective Django&lt;/a&gt; — testing covered already in the second chapter of the book&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://www.effectivedjango.com/orm.html#factoryboy-example&#34;&gt;Effective Django factory_boy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://carljm.github.io/django-testing-slides&#34;&gt;Django testing&lt;/a&gt; — excellent PyCon talk, slides covering pytest, fixtures vs factories, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;You should have a good idea about testing differences via unittest and pytest in the Django environment. The emphasis has been put on pytest (django-pytest) and some recommended approaches. The demo application django-testing brings functional test cases demonstrating the behaviour and features discussed. The articles and talks listed in this post were extremely helpful and instrumental in gaining expertise in the area and introducing rigorous testing approach into the production application.&lt;/p&gt;
&lt;p&gt;Any discrepancy between the behaviour described above and on your own setup may originate from different software versions. In any case, if anything is not clear enough, please let me know in the comments.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce in the Django World</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/12/ecommerce-in-django-world/"/>
      <id>https://www.endpointdev.com/blog/2014/12/ecommerce-in-django-world/</id>
      <published>2014-12-03T00:00:00+00:00</published>
      <author>
        <name>Kirk Harr</name>
      </author>
      <content type="html">
        &lt;p&gt;Mezzanine (&lt;a href=&#34;http://mezzanine.jupo.org/&#34;&gt;http://mezzanine.jupo.org/&lt;/a&gt;) is a powerful piece of software written for the Django Framework in Python that functions like a structured content management system similar to Drupal, WordPress, and others. Built on top of this CMS structure is a module which adds features for Ecommerce which is known as Cartridge (&lt;a href=&#34;http://cartridge.jupo.org/&#34;&gt;http://cartridge.jupo.org/&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;installing-cartridgemezzanine&#34;&gt;Installing Cartridge/Mezzanine&lt;/h3&gt;
&lt;p&gt;Installing the Mezzanine CMS system from scratch can be accomplished in two methods, either by using the pip python package manager, or you can clone the current source from the git repository from the software maintainers. If you were planning to modify either Mezzanine or Cartridge to customize the setup for your own needs the latter would likely be preferable as you could then easily begin creating custom branches off the original source to track the customization work. However for this example I will show the pip method of installation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install -U cartridge&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once installed, there is a mezzanine-project command which will allow you to create a new blank Mezzanine environment within a new directory, and in this case we will also send an option to instruct the command to install the Cartridge module as well.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mezzanine-project -a cartridge new_cartridge_project&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point you will have a blank Mezzanine environment with the cartridge module installed, now the Django database must be populated with the model information for the application, and then the Django application server will be started.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd new_cartridge_project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py createdb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py runserver&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all went well, you should see the startup messages for the Django application server which will list the version numbers of the various libraries it will use, and then should be up and listening on the loopback interface. At this point to complete the setup you would just need to point your httpd at this loopback socket to send connections there.&lt;/p&gt;
&lt;h3 id=&#34;cartridge-product-models&#34;&gt;Cartridge Product Models&lt;/h3&gt;
&lt;p&gt;Products within Cartridge are defined within the python models for the application, there are three primary models to be concerned with in a Product definition, the Product, ProductVariation and ProductImage models specifically.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product — Defines the primary attributes for a product like name, price, SKU, and can be populated with optional fields for sale prices, etc.&lt;/li&gt;
&lt;li&gt;ProductVariation — Defines a variant of a product SKU, these would be most commonly used for things like product sizes and colors.&lt;/li&gt;
&lt;li&gt;ProductImage — Defines the image for the picture of each product.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these three models exist within Django as their own data sets, but then they have foreign key references from each Product to all of its Variants and Images. While you can alter these model definitions using the django shell and other methods within python, you can also update and manage the products within the admin interface for the django site.&lt;/p&gt;
&lt;h3 id=&#34;adding-products--tracking-orders&#34;&gt;Adding Products / Tracking Orders&lt;/h3&gt;
&lt;p&gt;Adding products through the admin interface for Django was relatively easy and quick:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2014/12/ecommerce-in-django-world/image-0-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/12/ecommerce-in-django-world/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Within this interface you would provide the details for the products name, published status, date range for the product to be on the site, and product description in addition to any Variants or Images for the product. This is where the Mezzanine foundation of Cartridge start to show through, where this process mirrors the creating of a content page within Mezzanine, but has added these attributes of the product to the definition.&lt;/p&gt;
&lt;p&gt;In the same way, Product Variants, Discount Codes, and Sales can be created in much the same way within the admin interface. In this way, once you had a basic Cartridge setup in place on your server, within most use cases for setting up a simple web store, Cartridge would remove the need to do any further hacking of python code, and would allow any user familiar with a CMS workflow for creating and managing the objects within the environment to manage the store.&lt;/p&gt;
&lt;p&gt;In addition, Cartridge provides order management within the admin interface as well:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2014/12/ecommerce-in-django-world/image-1-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/12/ecommerce-in-django-world/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;From this interface you can update orders, create new orders, print the PDF invoices for the orders and track whether orders have been fulfilled and shipped to the customer.&lt;/p&gt;
&lt;h3 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;Like other Ecommerce applications built on top of a Content Management System, Cartridge and Mezzanine work well together to create a stack that allows for both ease of getting started with the system, and a massive degree of customization possible. Each component of Cartridge is defined within Django, and can be rewritten and customized to serve any number of use cases. The setup process for Cartridge was pretty straightforward, but one recommendation I can make is that once you have a working system up, you should place all of the files for Mezzanine and Cartridge into a source code management system like Git. Like other web applications of this type, the complexity of each change to the system can create a possible point of failure. Once you start making changes to the Mezzanine configuration files and the python for the Models and Views for Cartridge, controlling those changes becomes critical. In this way Mezzanine and Cartridge are spanning the normal continuum of software with the polar extremes of ease of use and customization by providing a good example to start with in the setup, and also exposing the full range of customization to the developer.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Integrate Twilio in Django</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2014/11/integrate-twilio-in-django/"/>
      <id>https://www.endpointdev.com/blog/2014/11/integrate-twilio-in-django/</id>
      <published>2014-11-24T00:00:00+00:00</published>
      <author>
        <name>Kulbir Singh</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;twilio&#34;&gt;Twilio&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.twilio.com/&#34;&gt;Twilio&lt;/a&gt; is a powerful HTTP API that allows you to build powerful voice and SMS apps. The goal of this blog post is to help make building the SMS applications as simple as possible in django.&lt;/p&gt;
&lt;p&gt;There is a already Twilio Python help library available. The open source &lt;a href=&#34;https://github.com/twilio/twilio-python&#34;&gt;twilio-python&lt;/a&gt; library lets us to write Python code to make HTTP requests to the Twilio API.&lt;/p&gt;
&lt;h3 id=&#34;installation&#34;&gt;Installation&lt;/h3&gt;
&lt;p&gt;The easiest way to install twilio-python library is using &lt;a href=&#34;http://pip.readthedocs.org/en/latest/quickstart.html&#34;&gt;pip&lt;/a&gt;. Pip is a package manager for Python.&lt;/p&gt;
&lt;p&gt;Simply run following command in terminal.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ pip install twilio&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;twilio-api-credentails&#34;&gt;Twilio API Credentails&lt;/h3&gt;
&lt;p&gt;To Integrate twilio API in django application, we need &lt;strong&gt;TWILIO_ACCOUNT_SID&lt;/strong&gt; and &lt;strong&gt;TWILIO_AUTH_TOKEN&lt;/strong&gt; variables. These variables can be found by logging into your Twilio account dashboard. These variables are used to communicate with the Twilio API.&lt;/p&gt;
&lt;p&gt;You’ll need to add them to your &lt;strong&gt;settings.py&lt;/strong&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TWILIO_ACCOUNT_SID = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TWILIO_AUTH_TOKEN = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;create-a-new-app&#34;&gt;Create a New App&lt;/h3&gt;
&lt;p&gt;We are going to interact with people using SMS, so I prefer to create an app named &lt;strong&gt;communication&lt;/strong&gt;. I am assuming you’ve already installed Django.&lt;/p&gt;
&lt;p&gt;Run following command in terminal.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ django-admin.py startapp communcation&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We will need to register the new app in our django project.&lt;/p&gt;
&lt;p&gt;Add it to your INSTALLED_APPS tuple in your settings.py file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSTALLED_APPS = (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;django.contrib.auth&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;django.contrib.sessions&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;django.contrib.sites&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;django.contrib.messages&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;‘&lt;/span&gt;communication&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;’&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;create-the-model&#34;&gt;Create the Model&lt;/h3&gt;
&lt;p&gt;Now we’ll open up &lt;strong&gt;communication/models.py&lt;/strong&gt; to start creating models for our app.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;SendSMS&lt;/span&gt;(models.Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    to_number = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;30&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    from_number = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;30&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sms_sid = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;34&lt;/span&gt;, default=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    account_sid = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;34&lt;/span&gt;, default=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created_at = models.DateTimeField(auto_now_add=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sent_at = models.DateTimeField(null=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    delivered_at = models.DateTimeField(null=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    status = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;, default=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;, blank=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;True&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and run the &lt;strong&gt;syncdb&lt;/strong&gt; command after defining the model:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ python manage.py syncdb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It will create the necessary database tables for our app.&lt;/p&gt;
&lt;h3 id=&#34;create-utilspy-file&#34;&gt;Create utils.py file&lt;/h3&gt;
&lt;p&gt;Create a new file named utils.py and save in &lt;strong&gt;communication/utils.py&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Put the following code in &lt;strong&gt;communication/utils.py&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.conf&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; settings
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;twilio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;twilio.rest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;send_twilio_message&lt;/span&gt;(to_number, body):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client = twilio.rest.TwilioRestClient(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; client.messages.create(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        body=body,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        to=to_number,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        from_=settings.TWILIO_PHONE_NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;testing-send_twilio_message&#34;&gt;Testing send_twilio_message&lt;/h3&gt;
&lt;p&gt;Open the &lt;strong&gt;shell&lt;/strong&gt; and run following commands.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;communication.utils&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; send_twilio_message
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; sms = send_twilio_message(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;+15005550006&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Hello Endpointer,&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; &lt;span style=&#34;color:#038&#34;&gt;print&lt;/span&gt; sms.sid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SM97f8ac9321114af1b7fd4463ff8bd038&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Having the sid means that everything in the backend is working fine. And we can proceed to work on the front end.&lt;/p&gt;
&lt;h3 id=&#34;create-form&#34;&gt;Create Form&lt;/h3&gt;
&lt;p&gt;Lets create a form to gather the data.  Now open/create up &lt;strong&gt;communication/forms.py&lt;/strong&gt; to start creating forms for our app. And paste the following code into it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;SendSMSForm&lt;/span&gt;(forms.ModelForm):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Meta&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        model = SendSMS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fields = (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;to_number&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-view-createview&#34;&gt;The View CreateView&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;SendSmsCreateView&lt;/span&gt;(CreateView):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    model = SendSMS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    form_class = SendSMSForm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template_name = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;communication/sendsms_form.html&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    success_url = reverse_lazy(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;send_sms&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;form_valid&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;, form):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        number = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.cleaned_data[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;to_number&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        body = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.cleaned_data[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;# call twilio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sent = send_twilio_message(number, body)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;# save form&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms = form.save(commit=&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms.from_number = settings.TWILIO_PHONE_NUMBER
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms.sms_sid = sent.sid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms.account_sid = sent.account_sid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms.status = sent.status
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms.sent_at = now()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; sent.price:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            send_sms.price = Decimal(force_text(sent.price))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            send_sms.price_unit = sent.price_unit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        send_sms.save()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;super&lt;/span&gt;(SendSmsCreateView, &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;).form_valid(form)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;defining-urls&#34;&gt;Defining URLS&lt;/h3&gt;
&lt;p&gt;The URL configuration tells Django how to match a request’s path to your Python code. Django looks for the URL configuration, defined as urlpatterns, in the &lt;strong&gt;urls.py&lt;/strong&gt; file in your project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.conf.urls&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; patterns, url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;.views&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; SendSmsCreateView
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;urlpatterns = patterns(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        regex=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^communication/send/sms/$&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        view=SendSmsCreateView.as_view(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;send_sms&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;creating-the-template&#34;&gt;Creating the Template&lt;/h3&gt;
&lt;p&gt;Now that we’ve defined a URL for our list view, we can try it out. Django includes a server suitable for development purposes that you can use to easily test your project:&lt;/p&gt;
&lt;p&gt;If you visit the &lt;strong&gt;http://127.0.0.1:8000/communication/send/sms/&lt;/strong&gt; in your browser, though, you’ll see an error: &lt;strong&gt;TemplateDoesNotExist&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is because we have not defined the template file yet. So now create &lt;strong&gt;sendsms_form.html&lt;/strong&gt; file in &lt;strong&gt;templates/communication/&lt;/strong&gt; and put the following code in it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;action&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;form-horizontal&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;method&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;role&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;form&amp;#34;&lt;/span&gt;&amp;gt;{% csrf_token %}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% for field in form %}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;form-group&amp;#34;&lt;/span&gt;&amp;gt;            &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;label&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;col-sm-2 control-label&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;for&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;inputEmail3&amp;#34;&lt;/span&gt;&amp;gt;{{ field.label_tag }}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;col-sm-10&amp;#34;&lt;/span&gt;&amp;gt;                {{ field }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                {{ field.errors }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;        &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;    {% endfor %}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;form-group&amp;#34;&lt;/span&gt;&amp;gt;        &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;col-sm-offset-2 col-sm-10&amp;#34;&lt;/span&gt;&amp;gt;            &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;btn btn-default&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&amp;gt;Submit&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;    &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;form&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now reload the &lt;strong&gt;http://127.0.0.1:8000/communication/send/sms/&lt;/strong&gt; in your browser. Assuming everything is okay, you should then see the following form:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2014/11/integrate-twilio-in-django/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2014/11/integrate-twilio-in-django/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Fill out the form, and hit the submit button to send your SMS.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;CONCLUSION&lt;/h3&gt;
&lt;p&gt;Congratulations, your SMS is successfully sent. Good luck!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Asynchronous Page Switches with Django</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/11/asynchronous-page-switches-with-django/"/>
      <id>https://www.endpointdev.com/blog/2013/11/asynchronous-page-switches-with-django/</id>
      <published>2013-11-19T00:00:00+00:00</published>
      <author>
        <name>Josh Williams</name>
      </author>
      <content type="html">
        &lt;p&gt;Now that the newly rebuilt &lt;a href=&#34;/&#34;&gt;endpoint.com&lt;/a&gt; website is up and running, you may have noticed it does something fancy: internal links within the site are fetched in the background, and the page is replaced dynamically with a script. That eliminates the ‘flicker’ of normal website navigation, and removes the need for the browser to re-parse CSS and JavaScript, making it feel more responsive.&lt;/p&gt;
&lt;p&gt;Recently I did some work on a Django project that uses jQuery for some AJAX calls to send information back to the database. It was a fairly simple $.post() call, but it got me thinking about Django’s template inheritance and how it could be used to render parts of templates and update those client-side without having to render the whole thing. The idea being, if your base template is complex and has a number of built-in queries or calculations, for example if you have a dynamic navigation menu, why put extra load on Postgres, on the web server, or have the browser reload the CSS, JS, images, or other resources, to load in what could be otherwise static data into a content column?&lt;/p&gt;
&lt;p&gt;The idea’s a little half-baked, just the result of a little after-hours tinkering over a couple evenings. Certainly hasn’t been fleshed out in a production environment, but seems to work okay in a test. I probably won’t develop it much more, but maybe the concepts will help someone looking for something similar.&lt;/p&gt;
&lt;p&gt;There’s a few options out there, apparently, between &lt;a href=&#34;https://github.com/apollo13/django-ajax-blocks&#34;&gt;django-ajax-blocks&lt;/a&gt; (which seems to do something similar to what I’m about to describe) and &lt;a href=&#34;http://tastypieapi.org/&#34;&gt;Tastypie&lt;/a&gt;, which lets you easily build with REST-based frameworks. Django’s usually pretty good about that, having projects available that build functionality on top of it. But not having researched those at the time, I put together this basic technique for doing the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a replacement template render function that detects AJAX-y requests.&lt;/li&gt;
&lt;li&gt;Update your page templates to use a variable in {% extends %}.*&lt;/li&gt;
&lt;li&gt;Create a simple XML base template with the blocks you use in your normal base template.&lt;/li&gt;
&lt;li&gt;Add a little JavaScript to perform the content switch.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Yes, this works, which was a bit of a surprise to me. It’s also the part I’m least happy with. More on that in a bit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The details&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;template-render-function&#34;&gt;Template Render Function&lt;/h3&gt;
&lt;p&gt;This takes after the handy django.shortcuts.render function. In fact, it leans on it fairly heavily.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;my_render&lt;/span&gt;(request, template, context={}, html_base=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main.html&amp;#39;&lt;/span&gt;, xml_base=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main.xml&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; request.is_ajax():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        context[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;base_template&amp;#39;&lt;/span&gt;] = xml_base
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; render(request, template, context, content_type=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;application/xml&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        context[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;base_template&amp;#39;&lt;/span&gt;] = html_base
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; render(request, template, context)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Giving html_base and xml_base as parameters lets views override those. This then injects a new variable, base_template, into the context passed to it with the appropriate base template.&lt;/p&gt;
&lt;h3 id=&#34;update-page-templates&#34;&gt;Update Page Templates&lt;/h3&gt;
&lt;p&gt;Your page templates, assuming they now at the top say {% extends &amp;lsquo;main.html&amp;rsquo; %}, replace with {% extends base_template %}. You shouldn’t have to make any other changes to it.&lt;/p&gt;
&lt;p&gt;But again, this is the bit I’m least happy about. It takes the selection out of the page template, and puts it in code. That takes away some of the decoupling an MVC environment like this is supposed to provide. Haven’t come up with a way around it, though.&lt;/p&gt;
&lt;h3 id=&#34;create-xml-base-template&#34;&gt;Create XML Base Template&lt;/h3&gt;
&lt;p&gt;In templates/main.xml (or whatever you want to call it above) create XML nodes for the blocks in your main.html file. Or at least the blocks your pages will replace:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;content&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;![CDATA[{% block title %}Django Site!{% endblock %}]]&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;main_content&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;![CDATA[{% block main_content %}{% endblock %}]]&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/main_content&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/content&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like your main.html, you can have defaults for the blocks here, such as a default title.&lt;/p&gt;
&lt;p&gt;Why XML? I’d originally envisioned using JSON, but it has escaping rules, of course. Django, so far as I’m aware, will always drop the contents of a block into place verbatim, without an opportunity to escape it into a JSON string. That’s where XML’s CDATA construct came in handy, allowing a segment of HTML to be put right in place. Assuming, of course, “]]&amp;gt;” doesn’t appear in your HTML.&lt;/p&gt;
&lt;h3 id=&#34;javascript-page-switches&#34;&gt;JavaScript Page Switches&lt;/h3&gt;
&lt;p&gt;That takes care of the back-end Django bit. The last bit involves the front end JavaScript that takes care of the page switch/content replacement. This example leans on jQuery fairly heavily. In essence we’ll: A) take over the link’s click event, B) send off an AJAX-type request for the same href, C) set up a callback to do the actual content switch. Or, to put it another way:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;a.ajax&amp;#39;&lt;/span&gt;).click(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; (e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Suppress the default navigate event
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  e.preventDefault();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Instead, do the GET in the background
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  $.get(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;.href).done(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; (response) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// The XML is automatically parsed and can be traversed in script
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; contentNodes = response.firstChild.childNodes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; i = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;; i &amp;lt; contentNodes.length; i++) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// Ignore any textNodes or other non-elements
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (contentNodes[i].nodeType != &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// Handle each XML element appropriately:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (contentNodes[i].nodeName == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;document&lt;/span&gt;.title = contentNodes[i].firstChild.nodeValue;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (contentNodes[i].nodeName == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main_content&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;#main_content&amp;#39;&lt;/span&gt;).html(contentNodes[i].firstChild.nodeValue);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;JavaScript, I’ll admit, isn’t a language I work in all that often. There’s probably a better way of parsing and handling that, but that seemed to work okay in testing. And, of course, it’s fairly bare bones as far as functionality. But it shouldn’t be difficult to add in a spinner, error handling, etc.&lt;/p&gt;
&lt;p&gt;Anyway, it was fun. Even the slightly frustrating update-JS/refresh/try-again cycle. Again it’s still fairly rough, and quite untested at any scale. But maybe the idea will help someone out there.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Getting the Django Admin to sort modified columns</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/09/getting-django-admin-to-sort-modified/"/>
      <id>https://www.endpointdev.com/blog/2013/09/getting-django-admin-to-sort-modified/</id>
      <published>2013-09-28T00:00:00+00:00</published>
      <author>
        <name>Phineas Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;A lot of what I’ve worked on at End Point is Ovis, a program to keep track of information about our servers. This information includes current operating system, data center, which client owns or uses it, etc. Ovis is built entirely on the Django Admin, with the most important information displayed on the Servers list page.&lt;/p&gt;
&lt;p&gt;A very important part of it is tracking server updates.&lt;/p&gt;
&lt;p&gt;Knowing when and by who a server was last updated is nice to see, but to put it to good use, we needed to have a column that would show when it was last updated and who last updated it. We also wanted this column to have links to the relevant pages.&lt;/p&gt;
&lt;p&gt;Django has a handy way of using functions to create a column based on data that is not part of the model being listed. The documentation for that is &lt;a href=&#34;https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display&#34;&gt;here.&lt;/a&gt; This is the code we are using:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;latest_update&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt; = &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.update_set.latest()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;a href=&amp;#34;&lt;/span&gt;%s&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;/a&amp;gt; by &amp;lt;a href=&amp;#34;&lt;/span&gt;%s&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;lt;/a&amp;gt;&amp;#34;&lt;/span&gt; % (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/admin/ovisapp/update/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&lt;/span&gt; % &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;.id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;.when_updated.date(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/admin/auth/user/&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&lt;/span&gt; % &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;.updated_by.id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;.&amp;#34;&lt;/span&gt; % (&lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;.updated_by.first_name, &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;.updated_by.last_name[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;except&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It ends up looking like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2013/09/getting-django-admin-to-sort-modified/image-0.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/09/getting-django-admin-to-sort-modified/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While this is very useful, it has one very big problem. You are unable to sort your list of objects by this new column. This is because with other columns, it sorts with an easy ORDER BY in the database. But when you are grabbing data from another table, adding links and extra space, words, etc., Django doesn’t know what to sort by.&lt;/p&gt;
&lt;p&gt;The main part of the sorting is creating a custom Manager to your model, and &lt;a href=&#34;https://docs.djangoproject.com/en/2.0/ref/models/querysets/#annotate&#34;&gt;annotating&lt;/a&gt; the QuerySet with &lt;a href=&#34;https://docs.djangoproject.com/en/2.0/ref/models/querysets/#aggregation-functions&#34;&gt;aggregate functions&lt;/a&gt;. Like this, which gets the newest update object for each server object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ServerManager&lt;/span&gt;(Manager):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;get_query_set&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        qs = &lt;span style=&#34;color:#038&#34;&gt;super&lt;/span&gt;(ServerManager, &lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;).get_query_set().annotate(Max(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;update__when_updated&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; qs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Set it as your model’s manager by adding this line to the model definition:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;objects = ServerManager()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, to get the column to sort by the aggregate function, set the &lt;em&gt;admin_order_field&lt;/em&gt; method attribute to point to your new annotated aggregate function, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;latest_update&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;latest_update.admin_order_field = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;update__when_updated__max&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it will sort by the date!&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2013/09/getting-django-admin-to-sort-modified/image-1.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2013/09/getting-django-admin-to-sort-modified/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Selenium Testing File Uploads in Django Admin</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/05/selenium-testing-file-uploads-in-django/"/>
      <id>https://www.endpointdev.com/blog/2013/05/selenium-testing-file-uploads-in-django/</id>
      <published>2013-05-15T00:00:00+00:00</published>
      <author>
        <name>Adam Vollrath</name>
      </author>
      <content type="html">
        &lt;p&gt;The Django framework version 1.4 &lt;a href=&#34;https://docs.djangoproject.com/en/dev/releases/1.4/#support-for-in-browser-testing-frameworks&#34;&gt;added much better integration with Selenium&lt;/a&gt; for in-browser functional testing. This made &lt;a href=&#34;https://web.archive.org/web/20130529183446/http://www.tdd-django-tutorial.com/&#34;&gt;Test-Driven Development&lt;/a&gt; an even more obvious decision for our new &lt;a href=&#34;https://github.com/LiquidGalaxy/lg-cms&#34;&gt;Liquid Galaxy Content Management System&lt;/a&gt;. This went very well until we needed to test file uploads in the Django admin interface.&lt;/p&gt;
&lt;p&gt;A browser’s file upload control has some unique security concerns that &lt;a href=&#34;https://stackoverflow.com/questions/1696877/how-to-set-a-value-to-a-file-input-in-html&#34;&gt;prevent JavaScript from setting its value&lt;/a&gt;. Trying to do so may raise INVALID_STATE_ERR: DOM Exception 11. Selenium’s WebDriver may sometimes &lt;a href=&#34;https://stackoverflow.com/a/10472542&#34;&gt;send keystrokes directly into the input element&lt;/a&gt;, but this did not work for me within Django’s admin interface.&lt;/p&gt;
&lt;p&gt;To work around this limitation, &lt;a href=&#34;http://www.rfk.id.au/blog/entry/testing-file-uploads-in-django/&#34;&gt;Ryan Kelly developed a Middleware to emulate successful file uploads for automated testing&lt;/a&gt;. This middleware inserts additional hidden fields into any forms sent to the client. Setting their value causes a file upload to happen locally on the server. (I used &lt;a href=&#34;https://bitbucket.org/proppy/playground/src/8f914e92a551/server/playground_editor/middleware.py&#34;&gt;a slightly newer version of this Middleware&lt;/a&gt; from &lt;a href=&#34;https://web.archive.org/web/20130123181028/http://playground.mekensleep.com/&#34;&gt;another project&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;However, &lt;a href=&#34;https://web.archive.org/web/20130502021707/http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions#Q:_Why_is_it_not_possible_to_interact_with_hidden_elements?&#34;&gt;Selenium intentionally will not interact with hidden elements&lt;/a&gt;. To work around this, we must send JavaScript to be executed directly in the browser using &lt;a href=&#34;https://docs.seleniumhq.org/docs/03_webdriver.jsp#using-javascript&#34;&gt;WebDriver’s execute_script method&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;.browser.execute_script(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;document.getElementsByName(&amp;#39;fakefile_storage&amp;#39;)[0].value=&amp;#39;placemark_end_point.kml&amp;#39;&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a lot of hoops to jump through, but now we have functional tests for file uploads and their post-upload processing. Hopefully the Selenium or Django projects can develop a better-supported method for file upload testing.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Making SSL Work with Django Behind an Apache Reverse Proxy</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2013/04/making-ssl-work-with-django-behind/"/>
      <id>https://www.endpointdev.com/blog/2013/04/making-ssl-work-with-django-behind/</id>
      <published>2013-04-09T00:00:00+00:00</published>
      <author>
        <name>Greg Davidson</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;bouncing-admin-logins&#34;&gt;Bouncing Admin Logins&lt;/h3&gt;
&lt;p&gt;We have a &lt;a href=&#34;https://www.djangoproject.com/&#34;&gt;Django&lt;/a&gt; application that runs on &lt;a href=&#34;http://gunicorn.org/&#34;&gt;Gunicorn&lt;/a&gt; behind an &lt;a href=&#34;https://httpd.apache.org/&#34;&gt;Apache&lt;/a&gt; reverse proxy server. I was asked to look into a strange issue with it: After a successful login to the admin interface, the browser was re-directed to the http (non-SSL) version of the interface.&lt;/p&gt;
&lt;p&gt;After some googling and investigation I determined the issue was likely due to our specific server arrangement. Although the login requests were made over https, the requests proxied by Apache to Gunicorn used http (securely on the same host). Checking the Apache SSL error logs quickly affirmed this suspicion. I described the issue in the #django channel on &lt;a href=&#34;http://freenode.net/&#34;&gt;freenode IRC&lt;/a&gt; and received some assistance from Django core developer &lt;a href=&#34;https://github.com/carljm&#34;&gt;Carl Meyer&lt;/a&gt;. As of Django 1.4 there was a new setting Carl had &lt;a href=&#34;https://code.djangoproject.com/ticket/14597#comment:16&#34;&gt;developed&lt;/a&gt; to handle this particular scenario.&lt;/p&gt;
&lt;h3 id=&#34;enter-secure_proxy_ssl_header&#34;&gt;Enter SECURE_PROXY_SSL_HEADER&lt;/h3&gt;
&lt;p&gt;The documentation for the &lt;a href=&#34;https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header&#34;&gt;SECURE_PROXY_SSL_HEADER&lt;/a&gt; variable describes how to configure it for your project. I added the following to the settings.py config file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SECURE_PROXY_SSL_HEADER = (&amp;#39;HTTP_X_FORWARDED_PROTO&amp;#39;, &amp;#39;https&amp;#39;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because this setting tells Django to trust the X-Forwarded-Proto header coming from the proxy (Apache) there are security concerns which must be addressed. The details are described in the Django documentation and this is the Apache configuration I ended up with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# strip the X-Forwarded-Proto header from incoming requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RequestHeader unset X-Forwarded-Proto
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# set the header for requests using HTTPS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RequestHeader set X-Forwarded-Proto https env=HTTPS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With SECURITY_PROXY_SSL_HEADER in place and the Apache configuration updated, logins to the admin site began to work correctly.&lt;/p&gt;
&lt;p&gt;This is standard practice for web applications that reside behind an HTTP reverse proxy, but if the application was initially set up using only plain HTTP, when HTTPS is later added, it can be easy to be confused and overlook this part of the setup.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>RailsAdmin &amp; Django</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/06/railsadmin-django/"/>
      <id>https://www.endpointdev.com/blog/2012/06/railsadmin-django/</id>
      <published>2012-06-14T00:00:00+00:00</published>
      <author>
        <name>Jeff Boes</name>
      </author>
      <content type="html">
        &lt;p&gt;Steph Skardal and Greg Davidson presented on RailsAdmin and Django (in the context of two of our clients, Musica Russica and Providence Plan).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/80083124@N08/7186728287/&#34; title=&#34;IMG_0796.JPG by endpoint920, on Flickr&#34;&gt;&lt;img alt=&#34;IMG_0796.JPG&#34; height=&#34;240&#34; src=&#34;/blog/2012/06/railsadmin-django/image-0.jpeg&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clients need a browser interface to administrate their Rails apps’ assets and configuration. RailsAdmin is an “engine” (an embedded miniature Rails app) for developing an admin interface, and a relatively young open-source project. It offers a CRUD-capable interface. It provides data export, filtering, pagination, and support for file attachments and a popular plug-in called “PaperTrail”. Musica Russica uses this as their site administration. This admin interface can be configured with many different customizable options: formatting, help text, sorting, etc.&lt;/p&gt;
&lt;p&gt;RailsAdmin also provides authorization to limit certain actions (“delete”, “read”, “manage”) to objects (“User”, “Order”) by user.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/80083124@N08/7371962564/&#34; title=&#34;IMG_0798.JPG by endpoint920, on Flickr&#34;&gt;&lt;img alt=&#34;IMG_0798.JPG&#34; height=&#34;320&#34; src=&#34;/blog/2012/06/railsadmin-django/image-1.jpeg&#34; width=&#34;240&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In contrast, Django, written in Python, dates back to 2005 and is aimed at complex web applications. It provides an automatic admin interface which of course you can extend and customize, and mostly the same feature set (pagination, filtering, etc). Several high-profile web applications (Instagram, Pinterest, and Mozilla’s add-ons site) are powered by Django. Our own work here is represented by the client site for Providence Plan.&lt;/p&gt;
&lt;p&gt;Django’s admin interface is (mostly) free of effort, once you have developed models for your application. In addition, there are many open-source apps that can be plugged into a project to manage specific things, such as permissions. Filters and searching can be configured, too: this helps limit the data that a user must consider during an administration operation.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Debugging Celery Tasks in Django Projects</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/03/debugging-celery-tasks-in-django/"/>
      <id>https://www.endpointdev.com/blog/2012/03/debugging-celery-tasks-in-django/</id>
      <published>2012-03-27T00:00:00+00:00</published>
      <author>
        <name>Greg Davidson</name>
      </author>
      <content type="html">
        &lt;p&gt;I recently had the opportunity to work on a &lt;a href=&#34;https://www.djangoproject.com/&#34;&gt;Django&lt;/a&gt; project that was using &lt;a href=&#34;http://www.celeryproject.org/&#34;&gt;Celery&lt;/a&gt; with &lt;a href=&#34;http://www.rabbitmq.com/&#34;&gt;RabbitMQ&lt;/a&gt; to handle long-running server-side processing tasks. Some of the tasks took several hours to complete. The tasks had originally been executed with the &lt;a href=&#34;https://linux.die.net/man/1/at&#34;&gt;at&lt;/a&gt; command and others had been managed with cron jobs. The client had started to migrate several of the tasks to use Celery when I joined the project.&lt;/p&gt;
&lt;p&gt;As I started to debug the Celery tasks, I quickly discovered there were many moving parts involved and it was not immediately clear which piece of the system was the cause of the problem. I was working in a local development environment that closely matched the production system. The dev environment consisted of a &lt;a href=&#34;https://pypi.org/project/virtualenv/&#34;&gt;virtualenv&lt;/a&gt; created using the same method Szymon Guz wrote about in his article &lt;a href=&#34;/blog/2012/02/django-and-virtual-environments_13/&#34;&gt;Django and virtualenvwrapper&lt;/a&gt;. Django, Celery and their related dependencies were installed with &lt;a href=&#34;https://pip.pypa.io/en/latest/&#34;&gt;pip&lt;/a&gt; and I installed RabbitMQ with &lt;a href=&#34;https://brew.sh/&#34;&gt;Homebrew&lt;/a&gt;. After activating my virtualenv and starting up everything up (or so I thought), I jumped in to an &lt;a href=&#34;https://ipython.org/&#34;&gt;IPython&lt;/a&gt; shell and began to debug the tasks interactively. Some tasks completed successfully but they finished almost instantaneously which didn’t seem right. The client had experienced the same issue when they excecuted tasks on their development server.&lt;/p&gt;
&lt;p&gt;Because I was joining an existing project in progress, the system administration and configuration had already been taken care of by other members of the team. Howerver, in the process of configuring my local development server to mimic the production systems, I learned a few things along the way, described below.&lt;/p&gt;
&lt;h3 id=&#34;rabbitmq&#34;&gt;RabbitMQ&lt;/h3&gt;
&lt;p&gt;RabbitMQ is a message broker; at its most basic it sends and receives messages between sender (publisher) and receiver (consumer) applications. It’s written in &lt;a href=&#34;http://www.erlang.org/&#34;&gt;Erlang&lt;/a&gt; which helps to make it highly parallel and reliable. The &lt;a href=&#34;http://www.rabbitmq.com/&#34;&gt;RabbitMQ web site&lt;/a&gt; is a great place to learn more about the project. For my purposes I needed to create a user and virtual host (vhost) and set up permissions for Celery to communicate with the RabbitMQ server. This was done with the &lt;a href=&#34;http://www.rabbitmq.com/rabbitmqctl.8.html&#34;&gt;rabbitmqctl&lt;/a&gt; command. I issued the following command to start up the server and let the process run in the background.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rabbitmq-server -detached&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I also enabled the &lt;a href=&#34;http://www.rabbitmq.com/management.html&#34;&gt;management plugin&lt;/a&gt; which provides both a web-based UI and a command line interface for managing and monitoring RabbitMQ. This is what the web-based UI looks like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-0.png&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-0.png&#34; width=&#34;750&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;django-celery&#34;&gt;django-celery&lt;/h3&gt;
&lt;p&gt;Celery works very well with Django thanks in large part to the &lt;a href=&#34;http://docs.celeryproject.org/en/latest/django/&#34;&gt;django-celery&lt;/a&gt; module. The django-celery module includes the djcelery app which can be plugged in to the Django admin site for your project. Connecting Django to Celery and RabbitMQ requires a few simple steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add djcelery to the list of INSTALLED_APPS in the settings.py file for the project.&lt;/li&gt;
&lt;li&gt;Add the following lines to settings.py:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;djcelery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;djcelery.setup_loader()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Create the celery database tables using the syncdb management command&lt;/li&gt;
&lt;li&gt;Configure the broker setttings in settings.py:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BROKER_HOST = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BROKER_PORT = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5672&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BROKER_USER = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;celery_user&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BROKER_PASSWORD = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;celery_password&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BROKER_VHOST = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;celery&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;celery&#34;&gt;Celery&lt;/h3&gt;
&lt;p&gt;With the RabbitMQ server up and running and Django configured to connect to Celery the last few steps involved starting up the Celery worker and its related monitoring apps. The Celery daemon (celeryd) has lots of options that you can check out by running the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py celeryd --help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For my purposes, I wanted Celery to broadcast events which the various monitoring applications could then subscribe to. It would also be good to print some helpful debugging info to the logs. I started up the Celery worker daemon with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py celeryd -E -loglevel=DEBUG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because I specified the -E flag, the celeryev application could be used to monitor and manage the Celery worker inside a terminal which was very helpful:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-1.png&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-1.png&#34; width=&#34;750&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For Django to capture and save Celery task information to the database, the celerycam application needs to be running. This command line app takes a snapshot of Celery every few seconds or at an interval you specify on the command line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py celerycam&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With celerycam running, the Django admin interface is updated as Celery tasks are executed:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-2.png&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-2.png&#34; width=&#34;750&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also view the detail for a particular task including any error messages from the task code:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-3.png&#34; target=&#34;_blank&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-3.png&#34; width=&#34;750&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With RabbitMQ, celeryd and celerycam ready to go, the Django development server could be started to begin testing and debugging Celery task code. To demonstrate this workflow in action, I wrote a simple Celery task that could be used to simulate how Django, Celery and RabbitMQ all work together.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;celery.decorators&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; task
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# A simple task to demonstrate Celery &amp;amp; djcelery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#555&#34;&gt;@task&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;add&lt;/span&gt;(x, y):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    delay = random.randint(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;,&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;60&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    time.sleep(delay)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; x + y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;tying-it-all-together&#34;&gt;Tying it all Together&lt;/h3&gt;
&lt;p&gt;With everything configured, I was ready to get to work debugging some Celery tasks. I set up a dashboard of sorts in &lt;a href=&#34;https://github.com/tmux/tmux&#34;&gt;tmux&lt;/a&gt; to keep an eye on everything as I worked on the code for a particular task:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-4-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/03/debugging-celery-tasks-in-django/image-4.png&#34; width=&#34;750&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clockwise from the bottom left you’ll see an IPython shell to debug the code interactively, the Django development server log, celeryev, Celery daemon (with debugging info) and the task code in Vim.&lt;/p&gt;
&lt;p&gt;When I started developing task-related code I wasn’t sure why my changes were not showing up in the Celery or Djcelery logs. Although I had made some changes, the same errors persisted. When I looked into this further I found that Celery caches the code used for a particular task and re-uses it the next time said task is executed. In order for my new changes to take effect I needed to restart the Celery daemon. As of Celery version 2.5 there is an option to have Celery autoreload tasks. However, the version of Celery used in this client project did not yet support this feature. If you find yourself working with Django, Celery and RabbitMQ, I hope you’ll find this helpful.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Downloading CSV file from Django admin</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/02/dowloading-csv-file-with-from-django/"/>
      <id>https://www.endpointdev.com/blog/2012/02/dowloading-csv-file-with-from-django/</id>
      <published>2012-02-22T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;Django has a very nice admin panel. The admin panel is highly extensible and there can be performed really cool enhancements. One of such things is a custom action.&lt;/p&gt;
&lt;p&gt;For the purpose of this this article I’ve created a simple Django project with a simple application containing only one model. The file models.py looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.db&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; models
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.contrib&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; admin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Stat&lt;/span&gt;(models.Model):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    code = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    country = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ip = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url = models.CharField(max_length=&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    count = models.IntegerField()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;StatAdmin&lt;/span&gt;(admin.ModelAdmin):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list_display = (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;code&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;country&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ip&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;count&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;admin.site.register(Stat, StatAdmin)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I’ve also added a couple of rows in the database table for this model. The admin site for this model looks like this:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2012/02/dowloading-csv-file-with-from-django/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2012/02/dowloading-csv-file-with-from-django/image-0.png&#34; width=&#34;600&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now I want to be able to select some rows and download a CSV file right from the Django admin panel. The file should contain only the information about selected rows.&lt;/p&gt;
&lt;p&gt;This can be done really easy with the admin actions mechanism. Over the table with rows there is the actions menu. There is one default action, it is “Delete selected stats”. To use the action you need to select the rows, select the action from the combo box and press the OK button.&lt;/p&gt;
&lt;p&gt;I will add another action there, which will be named &lt;em&gt;“Download CSV file for selected stats”&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;add-the-action&#34;&gt;Add the action.&lt;/h3&gt;
&lt;p&gt;First of all I will add the custom action. For this I will enhance the StatAdmin class with the field actions, and add the method, called when someone wants to run this action (all changes from the previous version are highlighted):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;StatAdmin&lt;/span&gt;(admin.ModelAdmin):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    actions = [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;download_csv&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    list_display = (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;code&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;country&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ip&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;count&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;download_csv&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;, request, queryset):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    download_csv.short_description = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Download CSV file for selected stats.&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the admin panel for the stats model you can notice that there is the new action. The above code doesn’t do anything useful, so let’s generate the CSV file.&lt;/p&gt;
&lt;p&gt;The whole idea is to generate the CSV file, don’t use any disk, do it in memory only and return to the user without any redirection to other page (the file should download automatically after pushing the OK button).&lt;/p&gt;
&lt;p&gt;Do it in small steps:&lt;/p&gt;
&lt;h3 id=&#34;generate-the-csv&#34;&gt;Generate the CSV&lt;/h3&gt;
&lt;p&gt;For this I will use the CSV module from Python’s standard library and the function now looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;download_csv&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;, request, queryset):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;csv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f = &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;some.csv&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    writer = csv.writer(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    writer.writerow([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;country&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;count&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; s &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; queryset:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        writer.writerow([s.code, s.country, s.ip, s.url, s.count])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After selecting two rows and running the action, it created a file some.csv in the main project directory with the following content.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;code,country,ip,url,count
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;B,BB,BBB,BBBB,22
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;C,CC,CCC,CCCC,33&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’s OK, generating the CSV file works, however it shouldn’t be stored on the disk.&lt;/p&gt;
&lt;p&gt;Return the file directly into the browser.&lt;/p&gt;
&lt;p&gt;I want to send the file to the client right after clicking on the OK button. This is fairly easy, the whole magic is to use proper HTTP headers. I will modify the method to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;download_csv&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;, request, queryset):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;csv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.http&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; HttpResponse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f = &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;some.csv&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wb&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    writer = csv.writer(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    writer.writerow([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;country&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;count&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; s &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; queryset:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        writer.writerow([s.code, s.country, s.ip, s.url, s.count])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f.close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f = &lt;span style=&#34;color:#038&#34;&gt;open&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;some.csv&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response = HttpResponse(f, content_type=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;text/csv&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Content-Disposition&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;attachment; filename=stat-info.csv&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; response&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So the main changes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;closed the file and reopen it for reading&lt;/li&gt;
&lt;li&gt;added headers for proper content type and file name.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is that when someone clicks on the OK button, the browser automatically starts downloading the stat-info.csv file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don’t use disk.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The only thing left: create the file in memory only. For this I will use StringIO module. It is a nice module implementing exactly the same interface as the file, so I can use it instead the file. StringIO operates only on memory without any disk operations.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;download_csv&lt;/span&gt;(&lt;span style=&#34;color:#038&#34;&gt;self&lt;/span&gt;, request, queryset):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;csv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;django.http&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; HttpResponse
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;StringIO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f = StringIO.StringIO()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    writer = csv.writer(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    writer.writerow([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;country&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ip&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;count&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; s &lt;span style=&#34;color:#080&#34;&gt;in&lt;/span&gt; queryset:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        writer.writerow([s.code, s.country, s.ip, s.url, s.count])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    f.seek(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response = HttpResponse(f, content_type=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;text/csv&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Content-Disposition&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;attachment; filename=stat-info.csv&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; response&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see the changes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;added import StringIO&lt;/li&gt;
&lt;li&gt;changed opening file to creating new StringIO object&lt;/li&gt;
&lt;li&gt;there is no reopening the file, only seek to set the marker at the beginning of the file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything is finished now. There is a new action in the admin panel which generates a new CSV file with information about chosen rows and it doesn’t do any browser redirection.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Django and Virtual Environments</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2012/02/django-and-virtual-environments_13/"/>
      <id>https://www.endpointdev.com/blog/2012/02/django-and-virtual-environments_13/</id>
      <published>2012-02-13T00:00:00+00:00</published>
      <author>
        <name>Szymon Lipiński</name>
      </author>
      <content type="html">
        &lt;p&gt;When you have to work with a bunch of different Python applications, the usual problem is that you have to deal with plenty of different packages in different versions. Each application needs its own set of libraries. Usually the versions of the libraries vary between the applications.&lt;/p&gt;
&lt;p&gt;To solve all the problems you could create Python virtual environments. There is a great tool: &lt;a href=&#34;http://pypi.python.org/pypi/virtualenv&#34;&gt;virtualenv&lt;/a&gt;. It can create virtual environments for Python. Using it is not too nice. However there is a wrapper to it, called &lt;a href=&#34;http://www.doughellmann.com/projects/virtualenvwrapper/&#34;&gt;virtualenvwrapper&lt;/a&gt;. It wraps all the virtualenv commands into a couple of shell commands.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s assume that I need to work on two applications written in Django 1.2 and Django 1.3. Each of the applications needs different set of packages in different versions. I will create two virtual environments.&lt;/p&gt;
&lt;p&gt;Installing virtualenvwrapper on Ubuntu is pretty easy:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo apt-get install virtualenvwrapper&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After the installation there is a couple of new commands. The basic one is: mkvirtualenv which creates a new environment. Let’s create one.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ mkvirtualenv django_demo_12&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command automatically switches to the new environment, so you might notice that the prompt changed. The prompt always starts with the name of current virtual environment.&lt;/p&gt;
&lt;p&gt;Let’s create another one, called django_demo_13 (to use Django 1.3 there).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$ mkvirtualenv django_demo_13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The list of environments is printed by the command workon, when called without arguments.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ workon
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django_demo_12
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django_demo_13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, there are two environments ready to use. You can pass the name of the virtual environment as parameter to the workon command. Now let’s install Django 1.2 on the environment django_demo_12.&lt;/p&gt;
&lt;p&gt;First of all switch to the new environment:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ workon django_demo_12&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the prompt changed, so you can always be sure which Python virtual environment you are using.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now Django should be installed. There is a couple of ways to install it. The one I prefer is to create a text file with names and versions of all needed packages. This file will be named requirements.txt and will contain only this one line so far (other packages will be added later):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Django==1.2.7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To install the packages listed in the file, I will use the command “pip install -r requirements.txt”:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$ pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Downloading/unpacking Django==1.2.7 (from -r requirements.txt (line 1))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Downloading Django-1.2.7.tar.gz (6.4Mb): 6.4Mb downloaded
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running setup.py egg_info for package Django
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Installing collected packages: Django
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Running setup.py install for Django
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changing mode of build/scripts-2.7/django-admin.py from 664 to 775
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;changing mode of /home/szymon/.virtualenvs/django_demo_12/bin/django-admin.py to 775
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Successfully installed Django
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Cleaning up...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I can check which Django version is installed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$ django-admin.py --version
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1.2.7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I will create a standard Django project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$ django-admin.py startproject django_demo_12&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only additional thing here is to move the requirements.txt file info the Django project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$ mv requirements.txt django_demo_12&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To create application using Django 1.3 the steps are similar. The first thing is to switch to another virtual environment:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_12)$ workon django_demo_13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From this moment it will be almost the same as in the previous environment, with the change that the requirements file should contain:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Django==1.3.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The commands are:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_13)$ pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_13)$ django-admin.py startproject django_demo_13
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_13)$ mv requirements.txt django_demo_13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So now there are two different Python environments, totally separated from each other. When I install something in one of them, it is not installed in the other, so I can have different packages for different Django versions.&lt;/p&gt;
&lt;p&gt;The best way to install a package here is to update the requirements.txt file, and run the “pip install -r requirements.txt” once again. Later it will be easier to give the whole code to another programmer, who then could run the command on his computer and it will automatically install all needed packages (each in exactly needed version).&lt;/p&gt;
&lt;p&gt;There is one simple command left. Sometimes you just want to remove the virtual environment from the path and use standard python libraries installed in the system. It can be done using this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(django_demo_13)$ deactivate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      </content>
    </entry>
  
    <entry>
      <title>Red Hat SELinux policy for mod_wsgi</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/10/selinux-httpd-modwsgi-26-rhel-centos-5/"/>
      <id>https://www.endpointdev.com/blog/2010/10/selinux-httpd-modwsgi-26-rhel-centos-5/</id>
      <published>2010-10-13T00:00:00+00:00</published>
      <author>
        <name>Adam Vollrath</name>
      </author>
      <content type="html">
        &lt;p&gt;Using SELinux, you can safely grant a process only the permissions it needs to perform its function, and no more.  Linux distributions provide policies to enforce these limits on most software they package, but many aren’t covered. We’ve made allowances for &lt;a href=&#34;https://code.google.com/archive/p/modwsgi/&#34;&gt;mod_wsgi&lt;/a&gt; on RHEL and CentOS 5 by extending Apache httpd’s SELinux policy.&lt;/p&gt;
&lt;p&gt;It seems the SELinux policy for Apache httpd is twice as large as any other package’s.  The folks at Red Hat have put a lot of work into making sure that attackers who manage to exploit httpd can’t break out to the rest of your system, while still allowing the flexibility to serve most applications.  Consult the &lt;a href=&#34;https://linux.die.net/man/8/httpd_selinux&#34;&gt;httpd_selinux man page&lt;/a&gt; if messages in audit.log coincide with your error.&lt;/p&gt;
&lt;h3 id=&#34;file-contexts&#34;&gt;File Contexts&lt;/h3&gt;
&lt;p&gt;If you’ve created files and/or directories in /etc/httpd, make sure they have the proper file contexts so the daemon can read them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# restorecon -vR /etc/httpd&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;httpd can only serve files with an explicitly allowed file context.  Configure the context of files and directories within your production code base using the semanage command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# semanage fcontext --add --ftype -- --type httpd_sys_content_t &amp;#34;/home/projectname/live(/.*)?&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# semanage fcontext --add --ftype -d --type httpd_sys_content_t &amp;#34;/home/projectname/live(/.*)?&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# restorecon -vR /home/projectname/live&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;View file contexts with ls -Z.  Changes should be generally accomplished with semanage and restorecon -vR.&lt;/p&gt;
&lt;h3 id=&#34;booleans&#34;&gt;Booleans&lt;/h3&gt;
&lt;p&gt;The httpd policy provides several boolean options for easy run-time configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;httpd_can_network_connect&lt;/strong&gt; — Allows httpd to make network connections, including the local ones you’ll be making to a database&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;httpd_enable_homedirs&lt;/strong&gt; — Allows httpd to access /home/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Booleans are persistently set using the setsebool command with the -P flag:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# setsebool -P httpd_can_network_connect on&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;wsgi-socket&#34;&gt;WSGI Socket&lt;/h3&gt;
&lt;p&gt;When running in daemon mode, httpd and the mod_wsgi daemon communicate via a UNIX socket file. This should usually have a context of httpd_var_run_t. The standard Red Hat SELinux policy includes an entry for /var/run/wsgi.* to use this context, so it makes sense to put the socket there using the WSGISocketPrefix directive within your httpd configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  WSGISocketPrefix run/wsgi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Note that run/wsgi translates to /etc/httpd/run/wsgi which is symlinked to /var/run/wsgi.)&lt;/p&gt;
&lt;p&gt;If socket communication fails, httpd returns a 503 “Temporarily Unavailable” error response.&lt;/p&gt;
&lt;h3 id=&#34;selinux-policy-module&#34;&gt;SELinux Policy Module&lt;/h3&gt;
&lt;p&gt;In the course of our testing SELinux denials like the following appeared:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  host=example.com type=AVC msg=audit(1262803154.315:1851): avc:  denied  { execmem } for  pid=5337 comm=&amp;#34;httpd&amp;#34; scontext=root:system_r:httpd_t:s0 tcontext=root:system_r:httpd_t:s0 tclass=process&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unusual behavior like this is usually best allowed by &lt;a href=&#34;https://fedoraproject.org/wiki/SELinux/LoadableModules/Audit2allow&#34;&gt;creating application-specific SELinux policy modules&lt;/a&gt;. If you cannot resolve these AVC errors by manipulating file contexts or booleans, collect all the errors into a single file and feed that into the audit2allow utility:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# yum install policycoreutils&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# mkdir ~/tmp  # if this doesn&amp;#39;t exist already&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# audit2allow --module wsgi &amp;lt; ~/tmp/pile_of_auditd_output &amp;gt; ~/tmp/wsgi.te&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will output source for a new policy module. You might review the .te file before compiling. Ours looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;module wsgi 1.0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;require {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      type httpd_t;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      class process execmem;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;#============= httpd_t ==============
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;allow httpd_t self:process execmem;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Compile this source into a new policy module and package it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# checkmodule -M -m -o ~/tmp/wsgi.mod ~/tmp/wsgi.te&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# semodule_package --outfile ~/tmp/wsgi.pp --module ~/tmp/wsgi.mod&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once created, the module may be installed permanently into any compatible system’s SELinux configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;# semodule --install ~/tmp/wsgi.pp&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s plenty of room for improvement here. The file contexts we assigned with semanage should be defined in a .fc source file and included within the policy module. And creating a new context just for the WSGI daemon to transition into would restrict it further, allowing only a subset of Apache httpd’s abilities. Writing your own policy like this allows you much finer tuning of your processes’ limits, while allowing their needed functionality.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Safari 4 Top Sites feature skews analytics</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2010/02/safari-4-top-sites-google-analytics/"/>
      <id>https://www.endpointdev.com/blog/2010/02/safari-4-top-sites-google-analytics/</id>
      <published>2010-02-13T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;https://www.apple.com/safari/&#34;&gt;Safari&lt;/a&gt; version 4 has a new “Top Sites” feature that shows thumbnail images of the sites the user most frequently visits (or, until enough history is collected, just generally popular sites).&lt;/p&gt;
&lt;p&gt;Martin Sutherland describes this feature in details and shows &lt;a href=&#34;https://web.archive.org/web/20100316231239/http://www.sunpig.com/martin/archives/2010/01/08/how-to-detect-a-page-request-from-safari-4s-top-sites-feature.html&#34;&gt;how to detect these requests&lt;/a&gt;, which set the X-Purpose HTTP header to “preview”.&lt;/p&gt;
&lt;p&gt;The reason this matters is that Safari uses its normal browsing engine to fetch not just the HTML, but all embedded JavaScript and images, and runs in-page client JavaScript code. And these preview thumbnails are refreshed fairly frequently—​possibly several times per day per user.&lt;/p&gt;
&lt;p&gt;Thus every preview request looks just like a regular user visit, and this skews analytics which see a much higher than average number of views from Safari 4 users, with lower time-on-site averages and higher bounce rates since no subsequent visits are registered (at least as part of the preview function).&lt;/p&gt;
&lt;p&gt;The solution is to simply not output any analytics code when the X-Purpose header is set to “preview”. In &lt;a href=&#34;/expertise/perl-interchange/&#34;&gt;Interchange&lt;/a&gt; this is easily done if you have an include file for your analytics code, by wrapping the file with an [if] block such as this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[tmp x_purpose][env HTTP_X_PURPOSE][/tmp]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[if scratch x_purpose eq &amp;#39;preview&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;!-- skip analytics for browser previews --&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(normal Google Analytics, Omniture SiteCatalyst, or other analytics code)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/else]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/if]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;a href=&#34;/expertise/ruby-on-rails/&#34;&gt;Ruby on Rails&lt;/a&gt; you’d check request.env[&amp;ldquo;HTTP_X_PURPOSE&amp;rdquo;].&lt;/p&gt;
&lt;p&gt;In PHP you’d check $_SERVER[&amp;ldquo;HTTP_X_PURPOSE&amp;rdquo;].&lt;/p&gt;
&lt;p&gt;In &lt;a href=&#34;/expertise/django-python/&#34;&gt;Django&lt;/a&gt; you’d check request.META[&amp;ldquo;HTTP_X_PURPOSE&amp;rdquo;] or the equivalent request.META.get(&amp;ldquo;HTTP_X_PURPOSE&amp;rdquo;) (from the &lt;a href=&#34;https://docs.djangoproject.com/en/dev/ref/request-response/&#34;&gt;HttpRequest&lt;/a&gt; class).&lt;/p&gt;
&lt;p&gt;And so on.&lt;/p&gt;
&lt;p&gt;I confirmed the analytics tracking code was omitted by waiting for Safari to make its preview request and inspecting the response with the &lt;a href=&#34;https://www.telerik.com/download/fiddler&#34;&gt;Fiddler proxy&lt;/a&gt;, on Windows. The same can be done for Safari on Mac OS X with a suitable Mac OS X HTTP proxy.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>DjangoCon 2009: Portland, Ponies, and Presentations</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2009/11/djangocon-2009-portland-ponies-and/"/>
      <id>https://www.endpointdev.com/blog/2009/11/djangocon-2009-portland-ponies-and/</id>
      <published>2009-11-04T00:00:00+00:00</published>
      <author>
        <name>Adam Vollrath</name>
      </author>
      <content type="html">
        &lt;p&gt;I attended &lt;a href=&#34;https://web.archive.org/web/20091110110923/http://www.djangocon.org/&#34;&gt;DjangoCon&lt;/a&gt; this year for the first time, and found it very informative and enjoyable. I hoped to round out my knowledge of the state of the Django art, and the conference atmosphere made that easy to do.&lt;/p&gt;
&lt;h3 id=&#34;presentations&#34;&gt;Presentations&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://about.me/avibryant&#34;&gt;Avi Bryant&lt;/a&gt;’s opening keynote was on the state of web application development, and what Django must do to remain relevant. In the past, web application frameworks did things in certain ways due to the constraints of CGI. Now they’re structured around relational databases. In the future, they’ll be arranged around Ajax and other asynchronous patterns to deliver just content to browsers, not presentation. To wit, “HTML templates should die”, meaning we’ll see more Gmail-style browser applications where the HTML and CSS is the same for each user, and JavaScript fetches all content and provides all functionality. During Q&amp;amp;A, he clarified that most of what he said applies to web applications, not content-driven sites which must be SEO-friendly and so arranged much differently. Many of these themes were serendipitously also in Jacob Kaplan-Moss’ “&lt;a href=&#34;https://jacobian.org/writing/snakes-on-the-web/&#34;&gt;Snakes on the Web&lt;/a&gt;” talk, which he gave at PyCon Argentina the same week as DjangoCon.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.ianbicking.org/&#34;&gt;Ian Bicking&lt;/a&gt;’s keynote was on open-source as a philosophy; very abstract and philosophical but also interesting. It has been &lt;a href=&#34;http://www.sauria.com/blog/2009/09/12/djangocon-2009/&#34;&gt;described&lt;/a&gt; as “a free software programmer’s midlife crisis”. &lt;a href=&#34;https://www.revsys.com/about/bio/frankwiles&#34;&gt;Frank Wiles&lt;/a&gt; of Revolution Systems gave a barn-burner talk on how Django converted him from Perl to Python, followed by another on &lt;a href=&#34;/expertise/postgresql/&#34;&gt;Postgres optimization&lt;/a&gt;. The latter reflected a theme that all &lt;a href=&#34;http://times.usefulinc.com/2008/06/16-ops-now&#34;&gt;web developers are now expected to do Operations&lt;/a&gt; as well, with several talks devoted to simple systems administration concepts.&lt;/p&gt;
&lt;h3 id=&#34;deployment&#34;&gt;Deployment&lt;/h3&gt;
&lt;p&gt;While working on Django projects we’ve been doing this year, I’ve been watching developments around deployment of Python web applications, particularly with Apache. The overwhelming consensus: Without active development, &lt;a href=&#34;http://www.modpython.org/&#34;&gt;mod_python&lt;/a&gt; is going the way of the dodo. Although &lt;a href=&#34;https://www.b-list.org/weblog/2009/aug/10/wsgi/&#34;&gt;WSGI is architecturally similar to CGI&lt;/a&gt;, the &lt;a href=&#34;https://collingrady.wordpress.com/2009/01/06/mod_python-versus-mod_wsgi/&#34;&gt;performance difference can be striking&lt;/a&gt;. &lt;a href=&#34;https://code.google.com/archive/p/modwsgi/&#34;&gt;mod_wsgi&lt;/a&gt;’s daemon mode running as a separate user is more secure and flexible than mod_python processes running as the Apache user. Given mod_wsgi’s momentum, it makes sense to use it and avoid mod_python for new projects.&lt;/p&gt;
&lt;p&gt;Several other tools kept re-appearing in presenters’ demonstrations. &lt;a href=&#34;http://www.fabfile.org/&#34;&gt;Fabric&lt;/a&gt; is a remote webapp deployment tool similar to Ruby’s &lt;a href=&#34;http://www.capify.org/&#34;&gt;Capistrano&lt;/a&gt;. Python’s package index, formerly named “&lt;a href=&#34;https://www.youtube.com/watch?v=B3KBuQHHKx0&#34;&gt;The Cheeseshop&lt;/a&gt;”, has been renamed &lt;a href=&#34;https://pypi.org/&#34;&gt;PyPI&lt;/a&gt;, the Python Package Index. Though easy_install is the standard tool to install PyPI packages, &lt;a href=&#34;https://pypi.org/project/pip/&#34;&gt;pip&lt;/a&gt; is gaining momentum as its successor. &lt;a href=&#34;https://pypi.python.org/pypi/virtualenv&#34;&gt;VirtualEnv&lt;/a&gt; is a tool to create isolated Python environments, discrete from the system environment. Since the conference I’ve been exploring how these tools may be leveraged for our own development, and may be integrated into our &lt;a href=&#34;http://www.devcamps.org/&#34;&gt;DevCamps&lt;/a&gt; multiple-environments system.&lt;/p&gt;
&lt;h3 id=&#34;pinax&#34;&gt;Pinax&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://eldarion.com/&#34;&gt;Eldarion&lt;/a&gt; folks gave three talks on &lt;a href=&#34;http://pinaxproject.com/&#34;&gt;Pinax&lt;/a&gt;, and the project came up a lot in conversations. If anything could be said to have “buzz” at the conference, this is it. Pinax is a suite of re-usable Django applications, encompassing functionality often-desired, but not common enough to be included in django.contrib. It may be compared to &lt;a href=&#34;https://www.drupal.org/&#34;&gt;Drupal&lt;/a&gt; and &lt;a href=&#34;https://plone.org/&#34;&gt;Plone&lt;/a&gt;. Its popularity also spurred discussions on what should or should not be included in the Django core, and how all Django developers should make their apps re-usable (some of James Bennett’s favorite topics).&lt;/p&gt;
&lt;h3 id=&#34;community&#34;&gt;Community&lt;/h3&gt;
&lt;p&gt;Among others, I was fortunate to spend time with &lt;a href=&#34;https://twitter.com/montylounge&#34;&gt;Kevin Fricovsky&lt;/a&gt; and the others who launched the new community site &lt;a href=&#34;https://web.archive.org/web/20090919170130/http://djangodose.com/&#34;&gt;DjangoDose&lt;/a&gt; during the conference. DjangoDose is a spiritual successor to the now-defunct &lt;a href=&#34;http://www.thisweekindjango.com/&#34;&gt;This Week in Django&lt;/a&gt; podcast, and was visible on many laptops in the conference room, aggregating &lt;a href=&#34;https://www.hashtags.org/analytics/djangocon/&#34;&gt;#djangocon tweets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That’s all I have time to relate now. There was plenty more there and I look forward to following up with people &amp;amp; projects.&lt;/p&gt;

      </content>
    </entry>
  
</feed>
