1425999327 flickr tie

First Steps in Django from Rails guy perspective (part 1)

Photo by John Hope

I am a Rails developer at Netguru, and every now and then, I get interested in other languages or frameworks that we can use to build web apps. Just recently, I experimented with node & express, but now I’d like to try some Python (and Django) and walk you through the process of what I experience as a Rails developer learning Django.

Keep in mind I’ve never written anything in Python before, and I only briefly glimpsed Django, so this will be a raw first impression. However, since I’m a Ruby / Rails developer, I expect to see some similarities between the two (which is probably terrible and will lead me astray).

Anyway, let’s get down to write a Django app! I’ll try to create a simple blog application which has posts that can be commented upon. In Rails, it’s about 10-15 minutes of work.

Version Manager

So Ruby has RVM, which is the first search result in Google with the ‘ruby version manager’ phrase, so I’d expect to see something similar for Python.

Searching for ‘python version manager’, however, shows me... stackoverflow - Is there a python version manager?!. After another quick search I found virtualenv, so I thought I’d give it a shot. The installation is via… pip install virtualenv, where the pip command comes from python, so it required me to have any python already installed on system to install it’s environment manager. According to virtualenv, I have to have at least pip 1.3 and I have 1.4, so we’re fine.

Let's install it:

Downloading/unpacking virtualenv
  Downloading virtualenv-1.10.1.tar.gz (1.3MB): 1.3MB downloaded
  Running setup.py egg_info for package virtualenv
    warning: no files found matching '*.egg' under directory 'virtualenv_support'
    warning: no previously-included files matching '*' found under directory 'docs/_templates'
    warning: no previously-included files matching '*' found under directory 'docs/_build'
Installing collected packages: virtualenv
  Running setup.py install for virtualenv
    warning: no files found matching '*.egg' under directory 'virtualenv_support'
    warning: no previously-included files matching '*' found under directory 'docs/_templates'
    warning: no previously-included files matching '*' found under directory 'docs/_build'
    Installing virtualenv script to /usr/local/bin
    Installing virtualenv-2.7 script to /usr/local/bin
Successfully installed virtualenv
Cleaning up...Raw

Ok, that went well, so we’ve got a python manager installed. It looks like we don’t have to add any additional lines to our .bashrc or .zsh files in order to load it up.

I already have python 2.7 installed on my mac, so I guess I don’t even need this version manager, at least now, since I’ll be using this version in my Blog anyway.

Installing Django

In order to create a new Rails app, you need to install a gem called ‘rails’ and then execute a command to bootstrap an empty skeleton of the app. Let’s find out how to do that in Python/Django.

It looks like Django has a nice tutorial on their site: Tutorial01

I’ll use this one here then. They say that the tutorial is written for Python 2.x, so I guess we’re fine at this point.

They assume I have Django already installed, and by running

python -c "import django; print(django.get_version())" it will show me the version. We do see this:

[18:03:21 rind]~<ruby-1.9.3-p392>$ python -c "import django; print(django.get_version())"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named djangoRaw

Ok so it looks like I don’t have Django... Let's install it, using this: Django project install

Install Django

So I already have that, I can skip this part.

  • Install Apache and mod_wsgi

    I don’t need that, since I’m only experimenting. Skip.

  • Get your database running

    I will need a database, let’s say I’ll use mysql for this blog. So it says I have to install the MySQL-python package… but how do I install it? Via pip? I’m not sure, let’s try it:

[18:06:50 rind]~<ruby-1.9.3-p392>$ pip install MySQL-python
Downloading/unpacking MySQL-python
  Downloading MySQL-python-1.2.4.zip (113kB): 113kB downloaded
  Running setup.py egg_info for package MySQL-python
    Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.28.tar.gz
    Extracting in /var/folders/cj/pl96sy6109n8vv6977n6ssgc0000gn/T/tmpiL_CVA
    Now working in /var/folders/cj/pl96sy6109n8vv6977n6ssgc0000gn/T/tmpiL_CVA/distribute-0.6.28
    Building a Distribute egg in /private/var/folders/cj/pl96sy6109n8vv6977n6ssgc0000gn/T/pip-build-rind/MySQL-python

Installing collected packages: MySQL-python
  Running setup.py install for MySQL-python
    building '_mysql' extension
    cc -fno-strict-aliasing -fno-common -dynamic -I/usr/local/include -I/usr/local/opt/sqlite/include -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -Dversion_info=(1,2,4,'final',1) -D__version__=1.2.4 -I/usr/local/Cellar/mysql/5.5.14/include -I/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c _mysql.c -o build/temp.macosx-10.7-x86_64-2.7/_mysql.o -g
    In file included from _mysql.c:44:
    /usr/local/Cellar/mysql/5.5.14/include/my_config.h:330:11: warning: 'SIZEOF_SIZE_T' macro redefined
    /usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/include/python2.7/pymacconfig.h:43:17: note: previous definition is here
    #        define SIZEOF_SIZE_T           8
    In file included from _mysql.c:44:
    /usr/local/Cellar/mysql/5.5.14/include/my_config.h:423:9: warning: 'HAVE_WCSCOLL' macro redefined
    #define HAVE_WCSCOLL
    /usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/include/python2.7/pyconfig.h:902:9: note: previous definition is here
    #define HAVE_WCSCOLL 1
    _mysql.c:1567:10: warning: comparison of unsigned expression < 0 is always false [-Wtautological-compare]
            if (how < 0 || how >= sizeof(row_converters)) {
                ~~~ ^ ~
    3 warnings generated.
    cc -bundle -undefined dynamic_lookup -L/usr/local/lib -L/usr/local/opt/sqlite/lib build/temp.macosx-10.7-x86_64-2.7/_mysql.o -L/usr/local/Cellar/mysql/5.5.14/lib -lmysqlclient_r -lpthread -lz -lssl -lcrypto -o build/lib.macosx-10.7-x86_64-2.7/_mysql.so

Successfully installed MySQL-python
Cleaning up...Raw
  • Ok so it did something, some errors / warnings came out, but in the end, I successfully installed MySQL-python. Well, at that point pip command works the same way as gem command for installing ruby gems. So I guess installing Django will be as easy as mysql package? We’ll see about that…

  • Remove any old versions of Django

    I don’t have any versions of Django, so I’ll skip this part

  • Install the Django code

    So it looks like pip install Django should do the case, let’s see how it goes:

[18:19:48 rind]~<ruby-1.9.3-p392>$ pip install Django
Requirement already satisfied (use --upgrade to upgrade): Django in /usr/local/lib/python2.7/site-packages
Cleaning up...Raw

It looks like something is wrong with my installation, since I never installed Django on this machine, let’s do what they ask me to:

[18:21:51 rind]~<ruby-1.9.3-p392>$ sudo pip install --upgrade Django
Downloading/unpacking Django from https://pypi.python.org/packages/source/D/Django/Django-1.5.4.tar.gz#md5=b2685469bb4d1fbb091316e21f4108de
  Downloading Django-1.5.4.tar.gz (8.1MB): 8.1MB downloaded
  Running setup.py egg_info for package Django

    warning: no previously-included files matching '__pycache__' found under directory '*'
    warning: no previously-included files matching '*.py[co]' found under directory '*'
Installing collected packages: Django
  Found existing installation: Django 1.3
    Uninstalling Django:
      Successfully uninstalled Django
  Running setup.py install for Django
    changing mode of build/scripts-2.7/django-admin.py from 644 to 755

    warning: no previously-included files matching '__pycache__' found under directory '*'
    warning: no previously-included files matching '*.py[co]' found under directory '*'
    changing mode of /usr/local/bin/django-admin.py to 755
Successfully installed Django
Cleaning up...Raw

I used sudo because my mac was crying about the permission when I ran this command without sudo the first time.

  • Ok! so it looks like we’ve got Django installed, let’s check it:
[18:24:02 rind]~<ruby-1.9.3-p392>$ python -c "import django; print(django.get_version())"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named djangoRaw
Still nothing, let’s see the paths that python is looking the packages for:
>>> import sys
>>> print sys.path
['', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC', '/Library/Python/2.7/site-packages']Raw
This array does not include the directory where my Django has been installed (which is /usr/local/lib/python2.7/site-packages, why this dir? No idea). After a quick search it looks like Python is looking for paths using PYTHONPATH environment variable, which I have modify in .bashrc or similar place to add a path. 
  • Ok, I’ve added the variable, let’s see if it works:
[18:32:26 rind]~<ruby-2.0.0-p0>$ python -c "import django; print(django.get_version())"
Great, so we’ve got 1.5.4 Django installed. Nothing unusual so far.

Setting up the app

According to the tutorial, I have to run django-admin.py startproject mysite command to setup new application. Cool, let’s run it:

[18:36:06 rind]~/projects/django<ruby-2.0.0-p0>$ django-admin.py startproject blog
[18:36:14 rind]~/projects/django<ruby-2.0.0-p0>$ ls
[18:36:16 rind]~/projects/django<ruby-2.0.0-p0>$ cd blog/
[18:36:18 rind]~/projects/django/blog<ruby-2.0.0-p0>$ ls
blog        manage.py
[18:36:19 rind]~/projects/django/blog<ruby-2.0.0-p0>$Raw

No errors, though it generated something. Let's see the tree:

[18:36:19 rind]~/projects/django/blog<ruby-2.0.0-p0>$ tree
├── blog
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.pyRaw

It looks like the initial skeleton. 5 files total. Seems nice and small. The new Rails app has many more directories.

The settings.py file looks like application.rb, the project configuration, although there is no division on various environments.

urls.py reminds me of routes.rb and probably that’s what it’s designed for, we’ll see about that later.

We don’t mind wsgi.py at the moment since we’re not setting up a webserver just yet. The manage.py file looks like equivalent to script/rails, we do various stuff with this command, like starting the server, syncing db (although in rails we do that via rake task).

Starting the app

Using manage.py, we will start up our server:

[18:37:25 rind]~/projects/django/blog<ruby-2.0.0-p0>$ python manage.py runserver
Validating models...

0 errors found
October 22, 2013 - 11:44:26
Django version 1.5.4, using settings 'blog.settings'
Development server is running at
Quit the server with CONTROL-C.Raw

Yay, it’s working! Here’s a proof: http://note.io/1h5vEEU

The equivalent for Rails will be a rails server command, very easy server startup.

Configuring the database

I’ve already created an empty database, named django_blog and created user django with password django, who has full access to this database.

Let’s configure the database. In the settings.py file, I’ll modify the DATABASES hash to:

    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_blog',
        'USER': 'django',
        'PASSWORD': 'django',
        'HOST': '',
        'PORT': '',

In the same file, I’ll also change TIME_ZONE to Europe/Warsaw.

The next step is the INSTALLED_APPS Array, which contains installed apps inside our app.

    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',

So it looks managing packages goes here. In Rails there’s a Gemfile file in which we store every gem we want to use for a specific version. At first glance, I can’t specify a version here, but there may be another way.

For now though, I’ll leave it as it is, since it’s the default Array for apps. Let’s first sync the db, as the tutorial says that some of these are using database tables. To sync the db we’re going to use the manage.py script:

[18:57:33 rind]~/projects/django/blog<ruby-2.0.0-p0>$ python manage.py syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'rind'):
Email address: [email protected]
Error: Enter a valid email address.
Email address: [email protected]
Password (again):
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)Raw

Interesting. It asked me for a Django superuser, and validated my random email, which appears to be invalid. Nice. I wonder why it needs this data though. In Rails we have migration files, which every contain changes that can be made to the database, like creating, changing, removing tables and sometimes changes to existing data. But I’ve never come across a migration that would ask for a user input.

Looking at the database, it actually created a record inside auth_user table with my credentials and marked me as a superuser.

Ok I guess we’re ready to use Django, but we will write controllers, models and views in second part or this blog post.

Thanks for reading,

On 04.12.2013 in Dev