{"id":1350,"date":"2010-05-12T15:20:28","date_gmt":"2010-05-12T20:20:28","guid":{"rendered":"http:\/\/mikeconley.ca\/blog\/?p=1350"},"modified":"2023-12-20T16:25:16","modified_gmt":"2023-12-20T21:25:16","slug":"python-eggs-sunny-side-up-and-other-goodies","status":"publish","type":"post","link":"https:\/\/mikeconley.ca\/blog\/2010\/05\/12\/python-eggs-sunny-side-up-and-other-goodies\/","title":{"rendered":"Python Eggs:  Sunny Side Up, and Other Goodies (or How I Learned to Stop Worrying and Start Coding)"},"content":{"rendered":"<h3>Cooking with Eggs<\/h3>\n<p>Every now and then, the computer gods smile and give me a freebie.<\/p>\n<p>I&#8217;ve been worrying my mind out over a few problems \/ obstacles for <a href=\"http:\/\/mikeconley.ca\/blog\/2010\/04\/27\/my-gsoc-project-review-board-extensions\/\">my Review Board extensions GSoC project<\/a>.\u00a0 In particular, I&#8217;ve been worrying about dealing with extension dependencies, conflicts, and installation.<\/p>\n<p>I racked my brain.\u00a0 I came up with scenarios.\u00a0 I drew lots of big scary diagrams on a wipe board.<\/p>\n<p>And then light dawned.<\/p>\n<h3>Batteries Come Included<\/h3>\n<p>Enter <a href=\"http:\/\/peak.telecommunity.com\/DevCenter\/setuptools\">Setuptools<\/a> and <a href=\"http:\/\/peak.telecommunity.com\/DevCenter\/PythonEggs\">Python Eggs<\/a>.<\/p>\n<p>All of those things I was worried about having to build and account for?\u00a0 When using Python Eggs, It&#8217;s all built in<em>.<\/em> Dependencies?\u00a0 Taken care of. Conflicts?\u00a0 Don&#8217;t worry about it.\u00a0 Installation?\u00a0 That&#8217;s what Setuptools and Python Eggs were built for!<\/p>\n<p><a href=\"http:\/\/peak.telecommunity.com\/DevCenter\/setuptools#extensible-applications-and-frameworks\">In fact, it even looks like Setuptools was designed with extensible applications in mind.<\/a><\/p>\n<h3>Wait, really?\u00a0 How?<\/h3>\n<p>Here&#8217;s the setup.py file for <a href=\"http:\/\/github.com\/chipx86\/rb-extension-pack\/tree\/master\/rbreports\/\">the rb-reports extension in the rb-extensions-pack on Github:<\/a><\/p>\n<pre>from setuptools import setup, find_packages\r\n\r\nPACKAGE=\"RB-Reports\"\r\nVERSION=\"0.1\"\r\n\r\nsetup(\r\n    name=PACKAGE,\r\n    version=VERSION,\r\n    description=\"\"\"Reports extension for Review Board\"\"\",\r\n    author=\"Christian Hammond\",\r\n    packages=[\"rbreports\"],\r\n    entry_points={\r\n        'reviewboard.extensions':\r\n        '%s = rbreports.extension:ReportsExtension' % PACKAGE,\r\n    },\r\n    package_data={\r\n        'rbreports': [\r\n            'htdocs\/css\/*.css',\r\n            'htdocs\/js\/*.js',\r\n            'templates\/rbreports\/*.html',\r\n            'templates\/rbreports\/*.txt',\r\n        ],\r\n    }\r\n)\r\n<\/pre>\n<p>Pay particular attention to the &#8220;entry_points&#8221; parameter.\u00a0 What this is doing, is registering rbreports.extension:ReportsExtension to the entry point &#8220;reviewboard.extensions&#8221;.<\/p>\n<p>&#8220;Hold up!&#8221;, I hear you asking. &#8220;What&#8217;s an entry point?&#8221;<\/p>\n<h3>Entry Points<\/h3>\n<p>An entry point is a unique identifier associated with an application that can accept extensions.<\/p>\n<p>The unique identifier for Review Board extensions is &#8220;reviewboard.extensions&#8221;.<\/p>\n<p>This is the first handshake, more or less, between Review Board and any extensions:\u00a0 in order for Review Board to &#8220;see&#8221; the extension, the extension must register an entry point at &#8220;reviewboard.extensions&#8221;.<\/p>\n<p><a href=\"http:\/\/reinout.vanrees.org\/weblog\/2010\/01\/06\/zest-releaser-entry-points.html\">This blog post shows how extensions can be found and loaded up.<\/a><\/p>\n<h3>Other Goodies<\/h3>\n<h4>INSTALLED_APPS and Django<\/h4>\n<p>I remember also being worried about how to create tables in Django for extension models.\u00a0 I thought &#8220;holy smokes, I&#8217;m going to have to either shoehorn some raw SQL into the extension manager, or maybe even trust the extension developers to write the CREATE TABLE queries themselves!&#8221;.<\/p>\n<p>Luckily, there&#8217;s a better alternative.<\/p>\n<p>Django knows about its applications through a dictionary called <a href=\"http:\/\/docs.djangoproject.com\/en\/dev\/ref\/settings\/#installed-apps\">INSTALLED_APPS.<\/a> When you add a new model to a Django project, you simply add the model app to the INSTALLED_APPS dictionary, and run &#8220;manage.py syncdb&#8221;.\u00a0 Django does the magic, bingo-bango, and boom &#8211; tables created.<\/p>\n<p>So if a new extension has some tables it needs created, I simply insert the app name of the extension into INSTALLED_APPS when the extension is installed, and <a href=\"http:\/\/groups.google.com\/group\/django-users\/browse_thread\/thread\/34b501d2d1f88496?pli=1\">call syncdb programmatically<\/a>.\u00a0 Tables created:\u00a0 no sweat.<\/p>\n<h4>django-evolution<\/h4>\n<p>Creating tables is easy.\u00a0 But what if an extension gets updated, and the table needs to be modified?\u00a0 Sounds like we&#8217;ve got a mess on our hands.<\/p>\n<p>And don&#8217;t expect Django to save you.\u00a0 When you modify a model in Django, they expect you to into that DB and alter that table by hand:<\/p>\n<blockquote><p>[syncdb] creates the tables if they don\u2019t yet exist. Note that syncdb does <em>not<\/em> sync changes in models or deletions of models; if you make a change to a model or delete a model, and you want to update the database, syncdb will not handle that.<br \/>\nFrom <a href=\"http:\/\/www.djangobook.com\/\">The Django Book<\/a> &#8211; <a href=\"http:\/\/www.djangobook.com\/en\/2.0\/chapter05\/\">Chapter 5: Models<\/a><\/p><\/blockquote>\n<p>Thankfully, there&#8217;s a mechanism that&#8217;s already built into Review Board that makes this trouble go away:\u00a0 <a href=\"http:\/\/code.google.com\/p\/django-evolution\/\">django-evolution<\/a>.\u00a0 Django-evolution, when used properly, will automatically detect changes in application models, and alter the database tables accordingly.\u00a0 This is how Review Board does upgrades.<\/p>\n<p>And to top that off, RB co-founder <a href=\"http:\/\/www.chipx86.com\/blog\/\">Christian Hammond<\/a> just became the django-evolution maintainer.<\/p>\n<p>Wow.\u00a0 Everything is falling neatly into place.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cooking with Eggs Every now and then, the computer gods smile and give me a freebie. I&#8217;ve been worrying my mind out over a few problems \/ obstacles for my Review Board extensions GSoC project.\u00a0 In particular, I&#8217;ve been worrying about dealing with extension dependencies, conflicts, and installation. I racked my brain.\u00a0 I came up [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[657,773],"tags":[687,685,688,689,690,674,684,675,683,686,679,680,678,131,92,676,682,681,673,637,671,677,1219,672],"class_list":["post-1350","post","type-post","status-publish","format-standard","hentry","category-extensions-review-board-code-reviews-computer-science-technology","category-gsoc-computer-science","tag-alter-tables","tag-call-syncdb-programmatically","tag-change-tables","tag-chipx86","tag-christian-hammond","tag-conflicts","tag-create-tables-dynamically","tag-dependencies","tag-django","tag-django-evolution","tag-entry-point","tag-entry_points","tag-extensible","tag-extensions","tag-gsoc","tag-installation","tag-installed_apps","tag-package_data","tag-pkg_resources","tag-plugins","tag-python-eggs","tag-rb-reports","tag-review-board","tag-setuptools"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/prmTy-lM","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts\/1350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/comments?post=1350"}],"version-history":[{"count":17,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts\/1350\/revisions"}],"predecessor-version":[{"id":1530,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/posts\/1350\/revisions\/1530"}],"wp:attachment":[{"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/media?parent=1350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/categories?post=1350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mikeconley.ca\/blog\/wp-json\/wp\/v2\/tags?post=1350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}