portable perspectives

a blog about programming, life, universe and everything

Rails vs Django: a non biased yet useless comparison

Lately I've been making my hands a little bit dirty with django, and since I already did a little bit of Rails I think I'm entitled to do a small comparison. No, this is a lie. I'm entitled to do that because I have a blog and that's it.

The non biased assertion is due to the fact that I like both frameworks and I do love both languages, the useless one is well, because you will still have to evaluate the things by yourself , cause I am not an expert at either and I think that my few readers are smart enough to understand that choosing an infrastructure requires more work than reading a random blog entry.

Both frameworks use the new classic MVC pattern (even if in django's lingo it's called MTV you will notice that it is 99.9% the same), and both framework provide some othe features, so I'll just split the comparison by these concepts.

* Model * The model layer in django and rails is basically built out of classes that map to DB tables, but the things are slightly different.

The model in rails is comprised of two things: an SQL definition and a Ruby definition. Yes I know about Dr Nic's magic models. You can also create the database in pure ruby by using the wonderful migrations' system that rails provides, but the model is still built out of the pair DB structure + Ruby code. AFAIK django doesn't have yet something on the level of Rails migrations, that allows you to programmatically move back and forth in time easily.

Django uses a single definition in python with more metadata, and provides some useful python datatypes that are mapped transparently on the DBMS, so that a Post class can have a SlugField, while a Comment can have EmailField and IPAddressField. Yet, you can still go down to the SQL level if you want.

It seem to me that the support for validation is quite similar under the surface for both frameworks: you can validate single fields ot whole objects at different times and stack different validators.

One big flaw in current django's models is that they don't allow inheritance, and so if you have a lot of models that differ just slightly you can't save work using this approach. Rails' ActiveRecord gets a big plus for this, but this should be solved soon in django.

* "the thing that does *which" ** Rails calls the module that chooses which data to show the Controller. For some reason Django calls it the View whereas the Controller is the underlying support software.

Rails which is made of two elements: Controller objects and actions. A request for a page is processed so that it instantiates a Controller object and setups variables appropriately, then the action method is called. At the class level you can define behaviour shared between the various action with a nice syntax, but I don't particularly love the :except and :only things.

In django an action is just a function which takes a fixed argument, the request, and an optional set of named arguments. I love the way named arguments work in python and I think that this approach provides much more readability, but to have that in ruby we will have to wait for ruby 2.0 (or use evil.rb which doesn't feel enterprisey).

The only thing I dislike in django's view is that you have to explicitly point out to a template file when rendering a view, whereas rails' convention over configuration in this case relieves the user of a repetitive and non-interesting operation.

I also think that not having the class Foo < Controller boilerplate is better, but the bad news is that django doesn't do magic requiring of modules and you have to do it yourself, so everything balance out.

Both systems allow caching but (for what I understood) Django mostly uses time-based cache invalidation, while rails provides only an event-based expiring behaviour.

Anyway there are plugins for rails that allow time based invalidation, and I believe that it should be possible to expire something in an event-based fashion in django too, cause the underlying mechanics are the same (CACHE.set(key,val), CACHE.get(key) and CACHE.del(key)).

It seems that django doesn't provide the fine-grained caching control that rails has, allowing just a whole-page caching or forcing the user to resort to the low level cache api, which I think is a bad thing.

Finally, django uses exceptions to handle error codes, which I believe is a far better solution than rails :status=>code, because it allows breaking out of an action from an utility method. This allows, the refactoring control in utility methods, having shortcuts such as get_object_or_404(Class,key=val), which simplify the writing of views.

* "the thing that does _how_" * The actual showing of data is usually done in both frameworks through templates, which rails call Views. Django uses an interestint template language while rails uses ruby embedded in XHTML by deafult, but different templating engines can be used as plugins very easily.

Both allow the composition of smaller units to enhance reuse and ease of maintenance and it I believe that apart from personal preferences there are not big differences in this area.

* Other things * I think that the main differences in rails and django can be appreciated going away from the basic elements and looking at the rest of the platform, where the frameworks differ greatly.

First, django provides internationalization as a builtin concept while in django you're forced to use external libraries such as Globalize. Sadly, I am convinced that this is such an important feature that it must be builtin and it is an error not to have it, because to do it right the framework must be engineered around it. It must be also noticed that python's support for unicode (and other text encodings) in general is far more advanced than ruby's, which basically just kind-of-supports utf-8 (wait for ruby2, again).

Second, django's automatic generation of an administration facility, complete with authorization support and a bunch of space for customization is amazing. Again, there are modules for rails that provide this kind of behaviour, but having such a feature builtin makes it a foundation and a reference against which every programmer can code, which makes coee sharing easier.

In general django has builtin support for a lot of functionalities that are common in modern web apps, not only admin and auth, but also sitemaps, syndication, comments and much more.

Rails takes a minimalistic approach on this and pushes all this stuff in plugins. The rails community is producing plugins at an incredible rate, and even if not all of them are compatible and high quality code there is a lot of choice.

In the old days rails' plugins used to have some issues and this was the reason for the existence of things like rails engines but as of 1.2 this problems are not there anymore, Django allows code sharing through the integration of different apps which is what the admin above mentioned features are, and each of this add-on can contain something as easy as a textilize() function to a whole application such as a wiki or a blog.

There are not so much django apps as there are rails plugins, and I believe that this depends on 3 factors

  • smaller community
  • slightly harder sharing (in rails you just user script/plugin install and you're done)
  • less duplication The latter is hardly dependent on django coming with more batteries included, so for example you don't need a plugin for creating slugs because there is a lready a slugify() function.

* Conclusion * I wish rails had more batteries included, and that ruby was a little bit more like python (i.e. modules, named args, function decorator syntax). And I wish django used more conventions/less typing, and python was more like ruby (blocks, functions call without parenthesis, implicit self). but I still like both, pick your choice or even better try both. This at least will make you ready to write your own framework ;)

See all comments

Understanding identity standards

this is just too funny not to deserve a link :)

See all comments

Rack 0.2 and Ramaze 0.1

Rack is a generic abstraction that allows web frameworks to use different servers (i.e. webrick, mongrel, CGI, FastCGI, SCGI, whatever). Basically, the ruby version of python'ìs WSGI. Using Rack you can avoid taking care of the details of framework interaction, and concentrate on developing your web app or framework. See announcement on chris' blog

Ramaze is a simple and clean web development framework loosely based on Nitro that makes use of Rack, has a lot of nifty things and just reached the 0.1 version.

What this two projects have in common? Real good testing. This is so refreshing after fighting with platforms and projects that have broken, incomplete or completely absent tests.

See all comments

A spell Corrector in perl6 part 3

(See part 1 and part 2 for explanations of the following code).

Now, this code should work. If I copy and paste it into pugs it works as expected. If I run it it has some failures in the final tests for correct() that I can't explain.

The part in the beinning shows how to count different words in a file, it depends on proper handling of unicode in pugs, so it may or may not work at the moment,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

sub words($file) { slurp($file).lc.comb(/<alpha>+/) }

sub train(@words) {
  my %res;
  for @words -> $w { %res{$w}++ }
  %res
}


#my %NWORDS = train(words('/home/rff/Desktop/big.txt'));
my %NWORDS={'ciao'=>4,'c'=>3,'cibo'=>1,'ciaao'=>1,'ccc'=>1,'cia'=>1};

my @ALPHA = 'a'..'z';


# 'abc' -> 'ac'
sub deletion($word) {
  (^$word.chars).map: {substr(my $tmp = $word,$_,1)='';$tmp};
}

# 'abc' -> 'adc'
sub substitution($word) {
  gather {
    for (0..$word.chars-1) X @ALPHA {
      substr(my $tmp = $word,$_[0],1)=$_[1];
      take $tmp;
    }
  }
}

# 'abc' -> 'abbc'
sub insertion($word) {
  gather {
    for (0..$word.chars) X @ALPHA {
      substr(my $tmp = $word,$_[0],0)=$_[1];
      take $tmp;
     }
  } 
}

# 'abc' -> 'acb'
sub transposition($w) {
  gather for ^$w.chars {
    my $tmp=$w;
    my $removed =(substr($tmp,$_,1)='');
    substr($tmp,$_+1,0)=$removed;
    take $tmp;
  }
}

sub edits1($w) {
  # all these are different, no need to use a set
  transposition($w),insertion($w),substitution($w),deletion($w)
}

    
sub known_edits2($words) { 
  my @ary = gather {
    for edits1($words) -> $e1 {
      for edits1($e1) -> $e2 {
        take $e2 if %NWORDS{$e2} 
      }
    }
  }
  any(@ary).values
}

sub known(@words) { 
  gather for @words {take $_ if %NWORDS{$_}} ;
}

sub correct($w) {
  my @values = known([$w]) or known(edits1($w)) or known_edits2($w) or [$w];
  # single argument max() doesn't work yet
  say @values.perl;
  @values.max: {%NWORDS{$^a} <=> %NWORDS{$^b}}

}

See all comments

A spell Corrector in perl6 part 2

In the first part we saw how we could write routines to generate string mutations by using perl6's regexen, but we also noticed that the implementation won't run on pugs, yet.

So, here is an alternative implementation of the same methods, based on Str.substr. The only thing to keep in mind is that this method does side effet modifying the current string, so in each case we have to create a copy (which for strings in perl we can do via simple assignment). The code should be quite self-explaining:


# 'abc' -> 'ac'
sub deletion($word) {
  (^$word.chars).map: {substr(my $tmp = $word,$_,1)=''; $tmp};
}

# 'abc' -> 'adc'
sub substitution($word) {
  gather {
    for (0..$word.chars-1) X @ALPHA {
      substr(my $tmp = $word,$_[0],1)=$_[1];
      take $tmp;
    }
  }
}

# 'abc' -> 'abbc'
sub insertion($word) {
  gather {
    for (0..$word.chars) X @ALPHA {
      substr(my $tmp = $word,$_[0],0)=$_[1];
      take $tmp;
     }
  } 
}

# 'abc' -> 'acb'
sub transposition($w) {
  gather for ^$w.chars {
    my $tmp=$w;
    my $removed =(substr($tmp,$_,1)='');
    substr($tmp,$_+1,0)=$removed;
    take $tmp;
  }
}

Remember that the value returned by the function is just the last computed one, so we always return implicitly the value of the gather (or of mao in the first function.

Now we can define a edits1 function that returns all the possible 1-letter variations of a word like this:


sub edits1($w) {
  transposition($w),insertion($w),substitution($w),deletion($w)
}

the comma operator concatenates the arrays so edits1 simply returns a big array of all the variations.

If you compare this code with norvig's you will notice that in edits1 he is already using Sets while we keep using simple lists. Since perl6 doesn't provide a builtin Set type we will use the any() junction which provide basically the same behaviour (i.e. we can check for the presence of an object, there are no duplicates and we can iterate over it).

Now we need a known(@array) routine that selects the elements of the list that actually exist. Rmember that we have an Hash variable %WORDS[word]=>number that we have initialized somehow with the existing english words and their frequency.

Our function is thus very easy:


sub known(@words) { 
  @words.grep:  { %NWORDS{$_}
}

The correct($word) function then becomes:


sub correct($word) {
  my @values =  known([$word]) or             # the word is known
                              known(edits1($word)) or # a single letter variation is known
                              [$word];                                # we don't have suggestions

  # single argument max() doesn't work yet
  @values.max: {%NWORDS{$^a} <=> %NWORDS{$^b}}
}

The last line coud be prettier, but I have the feeling the code is quite nice and clean.

For the last bit of code please wait a little bit more, but hey, it should be easy enough for you to write :)

See all comments

A spell Corrector in perl6 part 1

Back to blogging! Ok, I'll try to bring this blog back to his old glory^W^W^Wnormal activity, maybe not too much posting, but enough to be able to say that I actually have a blog in english.

At the moment I'm doing a little bit of perl6 hacking just to get familiar with all the new cool stuff that Larry Wall has been putting inside the language. I think that perl6 could be a lot simpler and use less punctuation, but I believe that a lot of the stuff that is being put into the design is extremely intersting.

So, after solving a couple of the 99problems I looked for something slightly bigger, and settled on a port of Norvig's spell corrector.

The main idea of the thing is quite simple: you build a huge database of existing words and their frequency, then correct("word") simply looks if "word" is in that database, or if there is a variation of it, or a variation of a variation, and returns the one with maximum frequency.

If nothing is found we surrender and return "word" unchanged.

The possible variations are:

  • insertion: 'helo' -> 'hello'
  • substitution: 'edd' -> 'odd'
  • deletion: 'housse' -> 'house'
  • transposition: ''hots' -> 'host'

In the python code these are implemented as list comprehensions plus handling string as arrays. Each variation is stored inside a Set object, which is an enumerable object that doesn't hold duplicates.

In perl6 the we could implement each of the operations easily with Str.subst(regexp,replacement), use gather+for for the iteration and result extraction, and use Junction objects in place of Sets.

Let's look at the for operations in detail:

1
2
3
4
# 'abc' -> 'ac'
sub deletion($w) {
    gather take $w.subst(/<at($_)>./,'') for ^$w.chars
}

The gather sets a kind of scope where take can operate. Each call to take puts the argument into an impicit array that is then returnd by gather. The for statement modifer iterates over an iterable object. The object is built from ^ which is the upto operator, which builds a range 0..N from a number N.

The regex is quite simple, too: at(N) is a zero-width assertion that matches at the N-th element, then we capture it with "." and replace it with nothing, deleting it.

Doing substitution is simple too:

1
2
3
4
# 'abc' -> 'adc'
sub substitution($w) {
    gather take $w.subst(/<at($_[0])>./,$_[1]) for $w.chars X @ALPHA
}

here the only difference is that we use the "X" operator, or cross opertator. The cross operator builds a cartesian product of two iterables, so iterating over 'a'..b' X 1..2 menas iterate over (('a',1),('a',2),('b',1),('b',2)). The inner code is then: replace N-th char with each one in the alphabet.

Insertion, on the same line, does the same thing withouth deleting a character, and doing one more iteration to add a character after the end of the string:

1
2
3
4
# 'abc' -> 'abbc'
sub insertion($w) {
    gather take $w.subst(/<at($_[0])>/,$_[1]) for ^($w.chars+1) X @ALPHA
}

Finally, trnasposition ues a slightly different form of subst, by passing a block, because it needs access to the capture objects:

1
2
3
4
# 'abc' -> 'acb'
sub transposition($w) {
    gather take $w.subst(/<at($_)>(.)(.)/,{"$1$0"})  for ^$w.chars
}

Basically just take chars N and N+1, and swap them.

Quite simple and nice implementations, in my opinion. The only problem is that at the moment pugs does not allow interpolation of variables in regex, so all this won't work.

For a working implementation, wait for part two of this article, or try to come up with your own :)

See all comments

AddThis Social Bookmark Button