Wednesday, April 11, 2012

Clearly and IReader - Safari Reader in your Chrome and Firefox

If you are a Safari user and use the "reader" function you will find such functionality missing in Google Chrome / Firefox.

IReader @ Chrome

Safari Reader is a nice builtin function that makes reading long articles easy. It removes banners so the article looks more like reading a plain book.
There are 2 extensions I found nice.

1. IReader (Chrome / Firefox )
2. Clearly (Chrome / Firefox )


Clearly @ Chrome
Try both. Haven't read the code, but seems like IReader parses the HTML code on site load what makes site loading slower (it's just my assumption). When IReader is available for the page, new icon appears in omnibox.
Clearly icon is always visible in the extensions box.

Yeah.. I'm a strong chrome extensions opponent.. it makes Chrome slooower.





Tuesday, March 27, 2012

Gunicorn + Nginx - a much better way to deploy your Django website

I've been a lighttpd + FastCGI django user for a long time.
Now there's a better solution -> gunicorn.

What is a gunicorn?
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.
If you are a fastcgi user, please check gunicorn asap. I'm sure you will get much more from your hardware :-)
Ps: Note that you get the most if database is not the bottleneck. That's why you should use cache everywhere if possible.

Here's my script for gunicorn + daemontools for supervising:
#!/bin/sh

# SITE_PATH is a directory where settings.py is stored
SITE_PATH=/var/www/django/projects/my_project/; export SITE_PATH
USER=www-data
TCP=127.0.0.1:8888

exec setuidgid $USER /usr/bin/gunicorn_django -b ${TCP} -w 3 --max-requests=1000 ${SITE_PATH}settings.py 2>&1

Seems like wsgi is also recommended with the new 1.4 version of Django
If you’re new to deploying Django and/or Python, we’d recommend you try mod_wsgi first. In most cases it’ll be the easiest, fastest, and most stable deployment choice.
Check django site for more information.

UPDATE:
Forgot to mention that I've switched from lighttpd to Nginx.
You can use the script above with the following nginx config:
server {
    listen   xx.xx.xx.xx:80;
    server_name domain.com www.domain.com;
    root /var/www/django/projects/my_project/public;
    access_log /var/log/nginx/domain-access.log;

    rewrite ^/admin(.*) https://domain.com/admin$1 permanent;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /media {
        root /var/www/django/projects/my_project/django/contrib/admin;
    }
    location /static {
        root /var/www/django/projects/my_project/site_media;
        access_log off;
    }
    location /site_media {
        root /var/www/django/projects/my_project;
    }
    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-For  $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 10;
        proxy_read_timeout 10;
        proxy_pass http://127.0.0.1:8888/;
    }

}

There's a trick you need to apply to get the real ip of the request source as seen in the line:
proxy_set_header X-Forwarded-For  $remote_addr;

Here's a code I'm using:
class XForwardedForMiddleware(object):
       def process_request(self, request):
        if "HTTP_X_FORWARDED_FOR" in request.META:
            ip = request.META["HTTP_X_FORWARDED_FOR"]
            if ip.startswith('::ffff:'):
                ip = ip[len('::ffff:'):]
            request.META["REMOTE_ADDR"] = ip
            request.META["REMOTE_HOST"] = None

Add XForwardedForMiddleware to your settings.py MIDDLEWARE_CLASSES setting.

Friday, March 2, 2012

Django HTML5 date input widget with i18n support and jQueryUI DatePicker fallback

Here's my manual for creating a HTML5 form date input with jQueryUI Datepicker and multilingual support.

What we need is:
1. django-floppyforms
2. jQueryUI Datepicker + jQuery 3. Modernizr javascript library
and of course - Django
You need to have some basic python, django & jquery knowledge.
Make sure multilingual support is enabled:
USE_I18N = True

and request context available for template
#settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
  # ...
  'django.core.context_processors.request',
  # ...
)
In your base template, you can use LANGUAGE_CODE variable.
<html lang="{{ LANGUAGE_CODE }}">

There are 3 ways to get this running

1. Totally django way - prividing django form & widget with the "lang" variable"

Your calendar widget needs to gain information about the language, so you need to pass a variable to your form, which then sends this variable to the widget.
def add_view(request):
    
    form = SomeForm(data = request.POST, lang=request.LANGUAGE_CODE)
    if form.is_valid():
        do_some_stuff()

from django import forms
from common.widgets import DatePicker

class SomeForm(forms.ModelForm):
    data = forms.CharField()
    when = forms.DateField()

    def __init__(self, *args, **kwargs):
        lang = kwargs.pop('lang')
        super(SomeForm, self).__init__(*args, **kwargs)
        
        self.fields['when'].widget = DatePicker(lang=lang)
import floppyforms as forms
from django.conf import settings

class DatePicker(forms.DateInput):
    template = "common/datepicker.html"

    def __init__(self, *args, **kwargs):
        self.lang = kwargs.pop('lang')
        super(DatePicker, self).__init__(*args, **kwargs)
    
    def get_context(self, name, value, attrs):
        ctx = super(DatePicker, self).get_context(name, value, attrs)
        ctx['lang'] = self.lang
        ctx['STATIC_URL'] = settings.STATIC_URL
        return ctx
The below, could be attached to python render() method in the widget, but I think this is more elegant way, and thanks to floppyforms we get native HTML5 date widget if available. I am aware of that floppyforms has an example of html5 datepicker, but the way the floppyforms author checks for date type doesn't seem to work - that's why I've used modernizr.
{# "common/datepicker.html" #}
{% include "floppyforms/input.html" %}

<script type="text/javascript">
    if (!Modernizr.inputtypes.date) {
       $(function () {
         $.datepicker.setDefaults($.datepicker.regional['{{ lang }}']);
         $('#{{ attrs.id }}').datepicker({showOn: "both", buttonImageOnly: true, dateFormat: "yy-mm-dd", buttonImage: "{{ STATIC_URL }}new/img/calendar.gif"})} )}
</script>

What the above code does is check if
<input type="date" />
is available. If not, it runs the jqueryUI calendar widget.
The javascript attached is to the specified selector. I've decided to go this way as I needed the calendar widget on ajax loaded content.
This is a Django (well.. not the sortest way).

2. Javascript way. Less django form messing.

You can achieve the similar effect without prividing both SomeForm & DatePicker widget with the lang variable.
import floppyforms as forms

class DatePicker(forms.DateInput):
    template = "common/datepicker.html"
Let the form know the widget it should use
from django import forms
from common.widgets import DatePicker

class SomeForm(models.Model):
    data = forms.CharField()
    when = forms.DateField(widget=DatePicker)
In the widget template code, you need jQuery for language detection.
{# "common/datepicker.html" #}
{% include "floppyforms/input.html" %}

<script type="text/javascript">
    if (!Modernizr.inputtypes.date) {
       
         $(function () {
         var lang = $('html').attr('lang');
         $.datepicker.setDefaults($.datepicker.regional[lang]);
         $('#{{ attrs.id }}').datepicker({showOn: "both", buttonImageOnly: true, dateFormat: "yy-mm-dd", buttonImage: "{{ STATIC_URL }}new/img/calendar.gif"})} )}
</script>
but I'm affraid this might not work on ajax loaded content where you need your calendar widget.

3. Django way, without messing the widget code too much.

Another way could be simply providing the form with data-lang attribute. The form would then look as follows:
from django import forms
from common.widgets import DatePicker

class SomeForm(forms.ModelForm):
    data = forms.CharField()
    when = forms.DateField()

    def __init__(self, *args, **kwargs)
        lang = kwargs.pop('lang')
        super(SomeForm, self).__init__(*args, **kwargs)

        self.fields['when'].widget = DatePicker(attrs={'data-lang': lang})
and then in the datepicker.html
{# "common/datepicker.html" #}
{% include "floppyforms/input.html" %}

<script type="text/javascript">
    if (!Modernizr.inputtypes.date) {
       $(function () {
         $.datepicker.setDefaults($.datepicker.regional[$('#{{ attrs.id }}').attr('data-lang'));
         $('#{{ attrs.id }}').datepicker({showOn: "both", buttonImageOnly: true, dateFormat: "yy-mm-dd", buttonImage: "{{ STATIC_URL }}new/img/calendar.gif"})} )}
</script>

The 2 ways above have no {{ STATIC_URL }} in widget template available. You need to get it yourself, via templatetag, or simply using hardcoded url.

Hope someone gets some tips from this manual

There for sure are many other ways, feel free to share them. Remember to include javascript modernizr, jQuery and jQuery files!

Thursday, February 9, 2012

Google Chrome slow? Disable extensions and get the real speed

I'm a Google Chrome fan since early versions. With every new version it consumed more memory. Today, after upgrading it to v. 17 I have decided to try Firefox v.10 What I saw was faster site rendering! Though FF seems to be less responsive (especially when switching tabs) sites were loading faster. It also  consumed less memory.

Finaly, I have disabled all chrome extensions with --disable-extensions switch. Viva Chrome! It's fast again!

If your chrome gets slower, disable extensions. You won't get fast page loads with extensions enabled.

Here's the extension list I was using:

  • Last Pass, 
  • Page Rank, 
  • Gmail Checker+, 
  • Firebug Lite, 
  • Smooth Gestures, 
  • RSS Finder, 
  • FlashBlock (that's the most suspicious one regarding the page load imho).
Question is "Is Google Chrome worth using without some of these extensions?" ...

Edit: You can also speed up Chrome with RAMDisk .
In my Chrome shortcut I added flags as follows:

--disk-cache-size=26214400  --media-cache-size=26214400 --disk-cache-dir=g:/ --purge-memory-button

This limits cache size to ~26 Megabytes.





Wednesday, February 1, 2012

Django language set in the database field applied on login

In one of my Django projects available only for logged in users I needed to set language setting in the database, so user can change the language setting in his profile.

Here comes a handy "user_logged_in" (Django >= 1.3) signal which lets us do things after user is logged in. In our case, we modify session and change the language.

My user_details.models.py:
from django.dispatch import receiver
from django.contrib.auth.signals import user_logged_in
from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.conf import settings

class UserProfile(models.Model):
    user = models.OneToOneField(User, blank=Flse)
    language = models.CharField(verbose_name=_(u"Interface Language"), 
                   max_length=4, choices=settings.LANGUAGES)

@receiver(user_logged_in)
def lang(sender, **kwargs):
    lang_code = kwargs['user'].userprofile.language
    kwargs['request'].session['django_language'] = lang_code


settings.py

gettext = lambda s: s

LANGUAGES = (
    ('pl',gettext('Polski')),
    ('en',gettext('English')),
    ('de',gettext('Deutsch')),
)

Sunday, July 5, 2009

django-sorting a generic way of queryset sorting


Just found a great project django-sorting. It's a generic way of queryset sorting.
It's based on middleware, and templatetag.

Please read the README.txt of the project.

In couple easy steps you get sorting:

1. Download & install django-sorting
2.In your settings.py
- add 'django-sorting' to INSTALLED_APPS
- add 'django_sorting.middleware.SortingMiddleware' to MIDDLEWARE_CLASSES
(make sure 'django.core.context_processors.request' is in your TEMPLATE_CONTEXT_PROCESSORS
3. In your template code: (the one below goes directly from README.txt

...
{% anchor first_name "First Name" %}
{% anchor last_name "Last Name" %}
{% anchor creation_date Creation flojax my_container %}
...

The first argument is a field of the objects list, and the second

one(optional) is a title that would be displayed. The previous

snippet will be rendered like this:


...
First Name
Last Name
Creation
...



It's BRILLIANT!


You should also use django-pagination for pagination support written by Eric as well.


Thank you ERIC!

Ps. Big thanks go to Karim (first author of django-sorting code) and other contributtors of these 2 great projects.

Monday, January 26, 2009

[PL] Authinfo i transfer domeny

Postanowiłem przenieść swoje domeny *.pl do innego registrara.

Jak wszyscy wiemy, niedawno NASK obniżył opłaty za przedłużenie domen,
która w przypadku domen *.pl wynosi 40 PLN netto. Obniżka cen miała miejsce niedawno.
Mowa o obniżce cen dla partnerów. Zwykły zjadacz domen dalej płaci 200 PLN netto, a raczej 244 pln brutto.

Jak można się domyśleć, nie przełożyło się to na obniżki cen domen u popularnych u nas firm rejestrujących domeny, jak home.pl itd - cena 99 pln netto (stan na 26.01.2009).

Wiadomo, każdy chce zarabiać.

Wcześniej transferowałem domenę *.com z firmy godaddy. Tam w 10 sekund, po wybraniu opcji "wyślij kod authinfo" otrzymałem emailem kod. Proste i przyjemne.

Niestety nie w Polsce.
Pierw wypełnienie formularza, wpisanie telefonu i powodu, dla którego chcemy authinfo. Można się domyśleć - bo chcę dokonać transferu (nie kłamałem, wpisałem).

Otrzymałem po 2 dniach emaila, z propozycją obniżenia opłaty za przedłużenie ze 100 do 80 pln netto. W emailu załączony był również wniosek. Wypełniłem, podpieczętowałem, przefaxowałem.

Następnie dostałem emailem info, że wniosek przyjęty i że mam zalogować się znowu do innej strony (podano mi login/hasło). Tam się zalogowałem i znowu kolejne 2 wnioski do wypełniania, podpisania, podpieczętowania i przefaxowania wraz dokumentami: NIP, regon, wpis do ewidencji.

Uwaga: wniosek dotyczy tylko jednej z dwóch domen *.pl
Do jednej dostałem już kod authinfo! Widocznie chcą mocnych dowodów, że ja to ja.

Szkoda tylko papieru, drzew. Zarabia na tym chociaż operator telekomunikacyjny,
bo faxy wysyłać trzeba.