{"id":279,"date":"2013-04-02T00:12:56","date_gmt":"2013-04-02T06:12:56","guid":{"rendered":"http:\/\/www.crccheck.com\/blog\/?p=279"},"modified":"2015-10-11T22:23:36","modified_gmt":"2015-10-12T04:23:36","slug":"dissecting-elevators-1-database","status":"publish","type":"post","link":"https:\/\/www.crccheck.com\/blog\/dissecting-elevators-1-database\/","title":{"rendered":"Dissecting Elevators part 1: rapid models.py development"},"content":{"rendered":"<blockquote><p><em>Intro<\/em>: This is part one of an eight part series looking at the <a href=\"http:\/\/elevators.texastribune.org\/\" target=\"_blank\">Elevator Explorer<\/a>, a fun data interactive mostly coded between the hours of 10 PM to 2 AM during the week leading up to April Fools&#8217; Day, 2013. I&#8217;m going to be looking at the things I learned, things I wish I could have done, and the reasoning behind my design choices. The code I&#8217;ll be referring to will be in this <a href=\"https:\/\/github.com\/texastribune\/tx_elevators\/tree\/2013-april-fools\">tagged release on github<\/a>.<\/p><\/blockquote>\n<h1>Rapid models.py development<\/h1>\n<h2>Introduction<\/h2>\n<p>Every minute doing boilerplate at the beginning of a project is a minute you&#8217;re not developing. And the beginning of a project is when you really need that momentum to code ideas, not fiddle with settings.\u00c2\u00a0 This post goes into how I do my database boilerplate.<\/p>\n<p>It is very common to use sqlite in early Django development because it&#8217;s so easy to migrate your database after editing your models. All you have to do is delete your old database file and run <code>syncdb<\/code> again. I even made a generic make command to find and delete all .sqlite files then run <code>syncdb<\/code> so I could reuse the same <code>make resetdb<\/code> command in every project. But what if you want to use another database like PostgreSQL? You could port to using <code>dropdb<\/code>\/<code>createdb<\/code>, but you may find it annoying to set up for every new project.\u00c2\u00a0 Luckily, there is a generic way thanks to <a href=\"https:\/\/github.com\/django-extensions\/django-extensions\">django-extensions<\/a> and <a href=\"https:\/\/github.com\/kennethreitz\/dj-database-url\">DJ-Database-URL<\/a>, both of which I include on every project. In my <a href=\"https:\/\/github.com\/texastribune\/tx_elevators\/blob\/2013-april-fools\/example_project\/settings.py#L25-L26\" target=\"_blank\">settings.py<\/a>, I still use sqlite by default with:<\/p>\n<pre><code>DATABASES = {'default':dj_database_url.config(default='sqlite:\/\/\/' +\r\n    project_dir('example_project.sqlite'))}\r\n<\/code><\/pre>\n<p>but in my virtualenv&#8217;s postactivate (or in your .env file), I have:<\/p>\n<pre><code>export DATABASE_URL='postgres:\/\/\/tx_elevators'\r\n<\/code><\/pre>\n<p>so it actually uses PostgreSQL. And by using django-extensions&#8217;s <code>reset_db<\/code> management command, the modifications to the makefile actually end up making things <em>simpler<\/em>:<\/p>\n<pre><code># old version:\r\n#   $(foreach db, $(wildcard $(PROJECT)\/*.sqlite),\\\r\n#      rm $(db);)\r\n#\r\n# new version:\r\nresetdb:\r\n    python $(PROJECT)\/manage.py reset_db --router=default --noinput\r\n    python $(PROJECT)\/manage.py syncdb --noinput\r\n<\/code><\/pre>\n<blockquote><p><a href=\"https:\/\/github.com\/texastribune\/tx_elevators\/blob\/2013-april-fools\/Makefile#L22-L24\" target=\"_blank\">https:\/\/github.com\/texastribune\/tx_elevators\/blob\/2013-april-fools\/Makefile#L22-L24<\/a><\/p><\/blockquote>\n<p>Just remember to put django-extensions in your <a href=\"https:\/\/github.com\/texastribune\/tx_elevators\/blob\/2013-april-fools\/example_project\/settings.py#L194\">installed apps<\/a>. And make sure you&#8217;re using a recent version of django-extensions (&gt;= 1.1.0), because there was a <a href=\"https:\/\/github.com\/django-extensions\/django-extensions\/commit\/dd4a675c4680a28a4185bd41fe9a00495d69c42f\">bug<\/a> in <code>reset_db<\/code> prior to then.<\/p>\n<p>Now after every model change, you can still use <code>make resetdb<\/code> to reset the db, no matter what database engine you use. Well&#8230; as long as that database engine is either sqlite, MySQL, or PostgreSQL.<\/p>\n<p style=\"text-align: center;\"><a href=\"http:\/\/www.crccheck.com\/blog\/wp-content\/uploads\/2013\/04\/anonymous-db.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-417 aligncenter\" title=\"An anonymous db\" alt=\"anonymous db\" src=\"http:\/\/www.crccheck.com\/blog\/wp-content\/uploads\/2013\/04\/anonymous-db-266x300.png\" width=\"266\" height=\"300\" srcset=\"https:\/\/www.crccheck.com\/blog\/wp-content\/uploads\/2013\/04\/anonymous-db-266x300.png 266w, https:\/\/www.crccheck.com\/blog\/wp-content\/uploads\/2013\/04\/anonymous-db.png 548w\" sizes=\"auto, (max-width: 266px) 100vw, 266px\" \/><\/a><\/p>\n<h2>Why not South?<\/h2>\n<p>South is a pain. Even if you script away the repetitiveness, it is an overly-complicated, blunt instrument designed for established projects, not for rapidly developing pre-alpha projects. And <em>wiping away the database<\/em> is a good thing. It means you can&#8217;t accumulate baggage in your data that you&#8217;ll never be able to recreate. Another benefit is that bootstrapping development on a new machine is a breeze because you&#8217;ve make that process simple and repeatable. And if you do need to do a quick migration, my first choice is django-extensions&#8217;s <a href=\"http:\/\/pythonhosted.org\/django-extensions\/sqldiff.html\"><code>sqldiff<\/code><\/a> command. For example, let&#8217;s say I wanted to make the <code>year_installed<\/code> date null-able to indicate bad data, and to make <code>Building.city<\/code> a foreign key to a new <code>City<\/code> model. If I changed my <a href=\"https:\/\/github.com\/texastribune\/tx_elevators\/blob\/2013-april-fools\/tx_elevators\/models.py\">original models.py<\/a> to do that, the output of <code>.\/manage.py sqldiff tx_elevators<\/code> becomes:<\/p>\n<pre><code>BEGIN;\r\n-- Application: tx_elevators\r\n-- Model: City\r\n-- Table missing: tx_elevators_city\r\n-- Model: Building\r\nALTER TABLE \"tx_elevators_building\"\r\n    DROP COLUMN \"city\";\r\nALTER TABLE \"tx_elevators_building\"\r\n    ADD \"city_id\" integer;\r\nCREATE INDEX \"tx_elevators_building_city_id_idx\"\r\n    ON \"tx_elevators_building\" (\"city_id\");\r\nALTER TABLE \"tx_elevators_building\"\r\n    ALTER \"city\" TYPE integer;\r\nALTER TABLE \"tx_elevators_building\"\r\n    ALTER COLUMN \"city\" SET NOT NULL;\r\n-- Model: Elevator\r\nALTER TABLE \"tx_elevators_elevator\"\r\n    ALTER COLUMN \"year_installed\" DROP NOT NULL;\r\nCOMMIT;\r\n<\/code><\/pre>\n<p>Which I can pipe into the database. You <em>do<\/em> have to know some SQL, because the SQL it produces is not always right, but it does get you 95% of the way there.<\/p>\n<h2>Afterwards<\/h2>\n<p>Once you do a release, you should abandon this and switch to using South. You can still use <code>make reset_db<\/code> and blow everything away, but you should at least be providing migrations once your project is stable.<\/p>\n<h2>Next time&#8230;<\/h2>\n<p>I&#8217;ll go over how I made importing data a one-liner.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Intro: This is part one of an eight part series looking at the Elevator Explorer, a fun data interactive mostly coded between the hours of 10 PM to 2 AM during the week leading up to April Fools&#8217; Day, 2013. I&#8217;m going to be looking at the things I learned, things I wish I could&hellip;<\/p>\n <a href=\"https:\/\/www.crccheck.com\/blog\/dissecting-elevators-1-database\/\" title=\"Dissecting Elevators part 1: rapid models.py development\" class=\"entry-more-link\"><span>Read More<\/span> <span class=\"screen-reader-text\">Dissecting Elevators part 1: rapid models.py development<\/span><\/a>","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[50,21,4],"tags":[51,54,48],"class_list":["entry","author-showmewhatyougot","post-279","post","type-post","status-publish","format-standard","category-best-practices","category-case-study","category-technical","tag-django","tag-makefiles","tag-python"],"_links":{"self":[{"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/posts\/279","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/comments?post=279"}],"version-history":[{"count":22,"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/posts\/279\/revisions"}],"predecessor-version":[{"id":751,"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/posts\/279\/revisions\/751"}],"wp:attachment":[{"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/media?parent=279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/categories?post=279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.crccheck.com\/blog\/wp-json\/wp\/v2\/tags?post=279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}