Django + nginx + uWSGI

I had to deploy a web application I just created using Django. I will describe the steps I followed.

  1. Make sure you have you Django setted up correctly. I followed this guide.
  2. Install uWSGI into your virtualenv:
    pip install uwsgi
    
  3. Install nginx:
    sudo aptitude install nginx
    sudo service nginx start
    
  4. Edit nginx.conf file (it is in /etc/nginx if you are using ubuntu), it should be something like this:
    upstream django {
     server unix:///path/to/your/mysite/mysite.sock;
    }
    
    server {
     listen 80 default_server;
     listen [::]:80 default_server ipv6only=on;
    
     server_name mysite.com;
    
     charset utf-8;
    
     # max upload size
     client_max_body_size 5M; # adjust to taste
    
     location /robots.txt {
     alias /path/to/your/mysite/robots.txt;
     }
    
     # Django media
     location /files {
     alias /path/to/your/mysite/files; # your Django project's media files - amend as required
     }
    
     location /static {
     alias /path/to/your/mysite/static; # your Django project's static files - amend as required
     }
    
     # Finally, send all non-media requests to the Django server.
     location / {
     uwsgi_pass django;
     include /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
     }
    }
    

    Make sure it matches your Django app settings.py configuration:

    PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'files')
    MEDIA_URL = '/files/'
    STATIC_ROOT = ''
    STATIC_URL = '/static/'
    
  5. Configure you uWSGI .ini file:
    # mysite_uwsgi.ini file
    [uwsgi]
    
    # Django-related settings
    # the base directory (full path)
    chdir = /path/to/your/mysite
    # Django's wsgi file
    module = mysite.wsgi
    # the virtualenv (full path) .virtualenvs at home folder
    home = /path/to/your/mysitevirtualenv  
    
    # process-related settings
    # master
    master = true
    # maximum number of worker processes
    processes = 10
    # the socket (use the full path to be safe
    socket = /path/to/your/mysite.sock
    # ... with appropriate permissions - may be needed
    chmod-socket = 666
    # clear environment on exit
    vacuum = true
    lgger = syslog
    
  6. Add uwsgi_params file to your project:
    uwsgi_param QUERY_STRING $query_string;
    uwsgi_param REQUEST_METHOD $request_method;
    uwsgi_param CONTENT_TYPE $content_type;
    uwsgi_param CONTENT_LENGTH $content_length;
    
    uwsgi_param REQUEST_URI $request_uri;
    uwsgi_param PATH_INFO $document_uri;
    uwsgi_param DOCUMENT_ROOT $document_root;
    uwsgi_param SERVER_PROTOCOL $server_protocol;
    uwsgi_param HTTPS $https if_not_empty;
    
    uwsgi_param REMOTE_ADDR $remote_addr;
    uwsgi_param REMOTE_PORT $remote_port;
    uwsgi_param SERVER_PORT $server_port;
    uwsgi_param SERVER_NAME $server_name;
    
  7. Run in emperor mode:
    # create a directory for the vassals
    sudo mkdir /etc/uwsgi
    sudo mkdir /etc/uwsgi/vassals
    # symlink from the default config directory to your config file
    sudo ln -s /path/to/your/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
    /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
    

    You can run the last command on screen or when the system starts.

And thats it, hope this can be helpful. You can find more info here.

Advertisements

Backbone.js & Knockout (English version)

I had a lot of work and things to do, so I couldn’t write.

The last year my boss asked me to look up all the libraries like Backbone.js and Knockout. The idea was to check all the options and take the more convenient.

To accomplish that I began to read and look a lot of information. Besides, I took the opportunity to try each one and feel them. For that, I made an application in Backbone.js and then observed how it would be using Knockout.

I will show you the code and then give my opinion. That way you will have a better decision.

Backbone.js

It’s really powerful and used by a lot of people, but it have an hard learning curve. You have a model and a bunch of things to define. If you follow all the conventions, the code will be easy to mantain. Here you can see more information and details.

It is ideal for REST web servicies.

Creating this application took me one noon and part of the morning.

The html:

<div>
    <fieldset>
        <legend>Person List - Using Backbone</legend>
        <div id="person-container">
            <input type="button" value= "Create New" class="Add" id="btn-create-new-person" />
            <table id="person-list">
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                    </th>
                </tr>
            </table>
        </div>
        <script id='person-template' type='text/template'>
            <td><%=ID%></td> <td><%=FirstName%></td> <td><%=LastName%></td> <td><%=Age%></td>
            <td><input type="button" value="Edit" class="person-edit" /> | <input type="button" value="Details" class="person-detail" /> | <input type="button" value="Delete" class="person-delete" /></td>
       </script>
    </fieldset>
</div>

Javascript:

$(function () {

    // Person Model
    var Person = Backbone.Model.extend({
        urlRoot: '/Person/List/',
        initialize: function () {
            console.log('Person initialize');
        },
        defaults: {
            ID: 0,
            FirstName: 'Unknown',
            LastName: 'Unknown',
            Age : 0
        }
    });

    // Person Collection
    var PersonCollection = Backbone.Collection.extend({
        model: Person,
        url: '/Person/List/'
     });

     // Person View - el returns the template enclosed within a tr
     var PersonView = Backbone.View.extend({
         template: _.template($('#person-template').html()),
         tagName: "tr",
         initialize: function () {
             console.log('PersonView initialize');
             this.model.bind('change', this.render, this);
             this.model.bind('remove', this.unrender, this);
         },
         render: function () {
             console.log('PersonView render');
             $(this.el).html(this.template(this.model.toJSON()));
             return this;
         },
         unrender: function () {
             console.log('PersonView unrender');
             $(this.el).remove();
             return this;
         },
         events: {
             "click .person-edit": 'EditPerson',
             "click .person-delete": 'DeletePerson'
         },
         EditPerson: function () {
             console.log('PersonView EditPerson');
             this.model.set({ FirstName: 'Unknown' });
             var self = this;
             this.model.save(this.model, { success: function () {
                 $("input:button", $(self.el)).button();
             }});
        },
        DeletePerson: function () {
            console.log('PersonView DeletePerson');
            this.model.destroy();
        }
    });

    // Actual App view
    var AppView = Backbone.View.extend({
        initialize: function () {
            console.log('AppView initialize');
            this.collection.bind('add', this.AppendPerson, this);
        },
        el: '#person-container',
        counter: 3,
        events: {
            "click #btn-create-new-person": "AddNewPerson"
        },
        AddNewPerson: function () {
            console.log('AppView AddNewPerson');
            this.counter++;
            var newPerson = new Person({ ID: this.counter, FirstName: 'Unknown ' + this.counter, LastName: 'Damn ' + this.counter, Age: this.counter });
            this.collection.add(newPerson);
            newPerson.save(newPerson, { success: function () {
                $("input:button", "#person-list").button();
            }});
        },
        AppendPerson: function (person) {
            console.log('AppView AppendPerson');
            var personView = new PersonView({ model: person });
            $(this.el).find('table').append(personView.render().el);
        },
        render: function () {
            console.log('AppView render');
            if (this.collection.length > 0) {
                this.collection.each(this.AppendPerson, this);
            }
            $("input:button", "#person-list").button();
        }
    });

    var persons = new PersonCollection();
    var view = new AppView({ collection: persons });

    persons.fetch({ success: function () {
        console.log('Fetch called');
        view.render();
    }});
});

In the server (my case asp.net MVC with C#):

public class PersonController : Controller
{
    List<Person> Persons = new List<Person> {
        new Person (1, "Federico", "Ponte" , 23),
        new Person (2, "Alejandro", "Drago" , 23),
    };

    // Get Person
    [AcceptVerbs(HttpVerbs.Get)]
    public JsonResult List(int? ID)
    {
        if (ID.HasValue)
        {
            Person PersonResult = Persons.Find(p => p.ID == ID);
            return Json(PersonResult, JsonRequestBehavior.AllowGet);
        }
        return Json(Persons, JsonRequestBehavior.AllowGet);
    }

    // Update Person
    [AcceptVerbs(HttpVerbs.Put)]
    public JsonResult List(int ID, Person UpdatedPerson)
    {
        Person PersonResult = Persons.Find(p => p.ID == ID);
        PersonResult.FirstName = UpdatedPerson.FirstName;
        PersonResult.LastName = UpdatedPerson.LastName;
        PersonResult.Age = UpdatedPerson.Age;
        return Json(PersonResult, JsonRequestBehavior.DenyGet);
    }

    //Add Person
    [AcceptVerbs(HttpVerbs.Post)]
    public JsonResult List(Person CreatePerson)
    {
        Persons.Add(CreatePerson);
        return Json(CreatePerson, JsonRequestBehavior.DenyGet);
    }

    //Delete Person
    [AcceptVerbs(HttpVerbs.Delete)]
    public JsonResult List(int ID)
    {
        Person PersonResult = Persons.Find(p => p.ID == ID);
        Persons.Remove(PersonResult);
        return Json(PersonResult, JsonRequestBehavior.DenyGet);
    }
}
public class Person
{
    public Person(int _ID, string _FirstName, string _LastName, int _Age)
    {
        ID = _ID;
        FirstName = _FirstName;
        LastName = _LastName;
        Age = _Age;
    }

    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

As you can see it is laborious, but it have advantages: highly configurable, it works on cellphones, you use web services. It is easy to scale with it.

It’s pretty tricky at the begging, but once you understand it, is simple.

Knockout

It use the pattern Model-View-View Model. It is quite easy to learn and pretty powerful. It comes with a complete template system and a lot of functions. You need to create a model and modify it directly in order to update the views. It have a pretty good documentation and it’s simple. To know more enter here.

One model would be:

function PersonModel () {
    var self = this;

    self.ID = ko.observable(0);
    self.FirstName = ko.observable('Unknown');
    self.LastName = ko.observable('Unknown');
    self.Age = ko.observable(0);
}

function PersonCollectionModel () {
    var self = this;

    self.Persons = ko.observableArray()
}

$(function (){
    ko.applyBindings(new PersonCollectionModel());
});

The functionality is pretty boring and it is not structurated. An example would be:

function PersonModel () {
    var self = this;

    self.ID = ko.observable(0);
    self.FirstName = ko.observable('Unknown');
    self.LastName = ko.observable('Unknown');
    self.Age = ko.observable(0);

    self.changeAndAlert = function (){
        self.FirstName('Hola');
        self.LastName('Chao');

        alert('Me cambie a ' + self.FirstName() + ' ' + self.LastName());
    }
}
<div>
    <fieldset>
        <legend>Person List - Using Knockout</legend>
        <div id="person-container">
            <input type="button" value= "Create New" class="Add" id="btn-create-new-person" />
            <table id="person-list">
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                    </th>
                </tr>
                <!-- ko foreach : persons -->
                    <tr>
                        <td data-bind="text : FirstName"></td>
                        <td data-bind="text : LastName"></td>
                        <td><input type="button" data-bind="click : changeAndAlert" value="ChangeAndAlert" /></td>
                    </tr>
                <!-- /ko -->
            </table>
        </div>
    </fieldset>
</div>

The rest of the functionality in the case of Backbone is quite easy to do. With a good jQuery knowledge or another library, it just take some Ajax calls and modify the model.

Comparision

Backbone is quite tricky and hard to learn. It gives maintainability and scalability.

I have used more Knockout and it is pretty simple.

At this time I am writting this i don’t have any preferences as before. Both can do the same, the decision is on what you need.

Others

This library is sponsored by Google.

Angular JS

Two useful pages i found

  1. Some days ago i stumbled with this page: http://twistedoakstudios.com/blog/

    When i was coding in .NET it was quite hard to find good information. Great part of the community have a low level of programming skills. I had to look so many pages, it was stressing.

    I think the problem relies on the UI way to do thing of Microsoft. People tend to forget how things work.

    The articles in that blog are from high level programmers: they don’t only code, but understand how it works. The most interesting part is that they use C# to do advanced things.

  2. The other page i’ll like to suggest is: http://www.blueprintcss.org/

    Awesome css framework. If you live web design, you will find everything you need there.

Dealing with IE

If you ask any web developer what they think about IE, they will answer it sucks. The reason is quite simple, you need to adapt the page to work on it. Besides, it doesn’t follow any standard well and have many bugs.The worst part of all is that at least one third of the Internet users use it.

In the past months i had been creating a web page and solved several problems regarding IE. Here are some tips:

  • css3 not working well on some IEs

    The best solution will be using different style sheets. Simulate some of css3 feature in css2. For example the border-radius can be solved using margin, padding and background attributes.

    If you just want to use the same style sheet for all, this will be pretty helpful: CSS3 PIE. But it won’t be as nice as the first one.

  • js issues

    Use a javascript framework and libraries well tested in all browsers like jQuery. It will solve a lot of the problems.

  • HTML5 tags

    This js library will help you a lot: Modernizr.

    For example if you want to simulate placeholder attribute, this code will save you:

    if (!Modernizr.input.placeholder){
        $('[placeholder]').focus(function () {
            var input = $(this);
            if (input.val() == input.attr('placeholder')) {
                input.val('');
                input.removeClass(tag);
            }
        }).blur(function () {
            var input = $(this);
            if (input.val() == '' || input.val() == input.attr('placeholder')) {
                input.addClass('placeholder');
                input.val(input.attr('placeholder'));
            }
        }).blur();
        $('[placeholder]').parents('form').submit(function () {
            $(this).find('[placeholder]').each(function () {
                var input = $(this);
                if (input.val() == input.attr('placeholder')) {
                    input.val('');
                }
            })
        });
    }
    

    The best you can do is trying to not use HTML5. It have tons of bugs on IE. Some useful links:

    https://code.google.com/p/html5shiv/
    http://stackoverflow.com/questions/3563107/jquery-html-vs-innerhtml

  • Testing

    The best way will be using VMs: http://www.microsoft.com/en-us/download/details.aspx?id=11575
    Other methods changing the browser mode and document type on IE: http://blogs.msdn.com/b/ie/archive/2010/10/19/testing-sites-with-browser-mode-vs-doc-mode.aspx

There will be one day when IE work as other browsers. For now i hope this save some people.