แสดงบทความที่มีป้ายกำกับ Development แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ Development แสดงบทความทั้งหมด

วันพฤหัสบดีที่ 26 ธันวาคม พ.ศ. 2556

Resource : AngularJS

Directive

an easy-understandable and practical tutorial

Scroll to Anchor

http://stackoverflow.com/questions/17711232/scroll-to-in-angularjs

Useful Pages

AngularJS Newsletter : many useful articles
http://www.ng-newsletter.com/posts/
my favorite, short one
http://www.ng-newsletter.com/advent2013/#!/

AngularJS tips (haven't check yet but likely useful)
http://angular-tips.com/

Using AngularJS + underscore.js
http://blog.mohammedlakkadshaw.com/AngularJS_Underscore_ultimate_web_development.html#.UrutMJDtnb4

A Good Explanation to Directive in AngularJS
http://www.ng-newsletter.com/posts/directives.html

Using AngularJS + jQuery chosen
http://onehungrymind.com/angularjs-chosen-plugin-awesome/
NOTE: change $watch --> $watchCollection

Difference between href and path()

$window.location.href = ('/actual/add');wll go back to server to fetch page, e.g. http://localhost:8000/actual/add, while
$location.path('/add');will lookup $routeProvider with to fetch content to fill <ng-view> tag
browser URL will show e.g. http://localhost:8000/actual#/add

Multiple Apps in Same Page

sample here >> http://plnkr.co/edit/UowJpWYc1UDryLLlC3Be?p=preview
instead of ng-app directive, use following code
<script>
angular.element(document).ready(function() {
        angular.bootstrap(document.getElementById('myApp1'), ['myApp1']);
        angular.bootstrap(document.getElementById('myApp2'), ['myApp2']);
});
</script>

วันพุธที่ 25 ธันวาคม พ.ศ. 2556

Cheat Sheet : Directives in AngularJS

Controller

$scope.api = {
    onCreate: function(tag) { console.log('add NEW tag', tag); },
    onChange: function(qid, change) { $scope.onTagChange(qid, change); },
}

$scope.testFn = function(obj) {
    console.log('testFn recv ', obj);
}


$scope.onTagChange = function(qid, change) {
    ...
}

Directive

app.directive('chosen',function($parse){
    var linker = function(scope, element, attrs) {
            // example for isolated scope
            console.log(scope.index); // use variable passed from tag
            scope.testFn({obj: change}); // call function with parameter from directive
            scope.api.onChange(scope.question, change); // call function in object passed from tag
            scope.callback()(scope.index, change); // call function passed from tag

            // example for inherited scope
            scope.onTagChange(scope.$index, change); // directly call function from parent scope (in case not using isolated scope)
    };

    return {
        restrict:'A',
        link: linker,
        scope: {index: '=',
                callback: '&chosenChange', // rename in local : chosenChange --> callback
                api: '=chosenApi', // rename in local : chosenApi --> api
                testFn: '&'},
    }

HTML


<div chosen="choice.subtags" chosen-change="onTagChange" chosen-api="api" test-fn= "testFn(obj)" index="$index">

Another Example

Controller

$scope.value = {a: 10};
$scope.inc = function(){
    $scope.value.a += 1;
}
$scope.dec = function(){
    $scope.value.a -= 1;
}

Directive

app.directive('ngSparkline', function() {
  return {
    restrict: 'A',
    scope: {value: '=value', inc: '&incFn', dec: '&decFn'},
    link: linker,
    controller: ['$scope', function($scope) {
        $scope.value8 = $scope.value * 8;
    }],
    template: '<input type="text" ng-model="value8" placeholder="Enter a value" ng-click="inc()" ng-blur="dec()" />',
  }
});

HTML

<div ng-sparkline value="value.a" inc-fn="inc()", dec-fn="dec()"></div>

วันเสาร์ที่ 21 ธันวาคม พ.ศ. 2556

mysqlsla v2 : MySQL Slow Query Log Analyzer

Reference

Installation

  1. Download mysqlsla-2.03.tar.gz
  2. tar xvfz mysqlsla-2.03.tar.gz
  3. cd mysqlsla-2.03
  4. perl Makefile.PL
  5. make
  6. sudo make install

Cheat Sheet

analyze slow query log
    mysqlsla -lt slow /var/log/mysql/mysql-slow.log
analyze slow query log from specified user
    mysqlsla -lt slow /var/log/mysql/mysql-slow.log -mf user=user1
analyze slow query log with rows examined > 10
    mysqlsla -lt slow /var/log/mysql/mysql-slow.log -mf 're>10'
analyze top 3 slow query log from specified user
    mysqlsla -lt slow /var/log/mysql/mysql-slow.log -mf user=user1 --top 3

analyze slow query log filter only select statement
    mysqlsla -lt slow /var/log/mysql/mysql-slow.log -sf +select

analyze slow query log filter only non-select statement
    mysqlsla -lt slow /var/log/mysql/mysql-slow.log -sf -select

MySQL 5.5 : Slow Query Log

for MySQL 5.5 to log slow queries, setting in /etc/mysql/my.cnf

log-queries-not-using-indexes = 1
log-slow-queries=1
slow_query_log_file   = /var/log/mysql/mysql-slow.log
long_query_time = 1

reference : http://dev.mysql.com/doc/refman/5.5/en/slow-query-log.html

วันศุกร์ที่ 20 ธันวาคม พ.ศ. 2556

Promise and Deferred in AngularJS


Reference


Very simple example of using promises
http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/

An example of using notify() to update progress
http://nurkiewicz.blogspot.com/2013/03/promises-and-deferred-objects-in-jquery.html

Official document of AngularJS promise/deferred
http://docs.angularjs.org/api/ng.$q


compare code : callback .vs. promise


CallbackPromise
in function
var getMessages = function(callback)
{
    $timeout(function() {
        callback(['Hello', 'World!']);
    }, 2000);
}
in function
var getMessages = function()
{
    var deferred = $q.defer();
    $timeout(function() {
        deferred.resolve(['Hello', 'World!']);
    }, 2000);
    return deferred.promise;
}
in controller
getMessages(function(messages) {
    $scope.messages = messages;
});
in controller
getMessages().then(function(messages) {
    $scope.messages = messages;
});

promise with notify()


in function
var getMessages = function() {
    var deferred = $q.defer();

    var id = setInterval(function() {
        deferred.notify();
    }, 1000);

    $timeout(function() {
        clearInterval(id);
        deferred.resolve(['Hello', 'World!']);
    }, 9000);
    return deferred.promise;
}


in controller
HelloWorldP.getMessages().then(
function(messages) { // function for resolve() callback
    $scope.p.messagesp = messages;
}, 
null, // function for error callback
function(){// function for notify() callback
    $scope.p.waiting_msg += '.';
});

วันพุธที่ 13 พฤศจิกายน พ.ศ. 2556

Steps to Initiate Django project in Eclipse

1. create project folder
cd ~/workspace
django-admin.py startproject myproject
cd myproject
python manage.py startapp myapp


<< result >>
~/workspace
   myproject
      myproject
         __init__.py
         settings.py
         urls.py
         wsgi.py
      manage.py
      myapp
         __init__.py
         models.py
         views.py
         tests.py


2. create project in Eclipse
Eclipse menu > File > New > PyDev Project
   Project name = myproject
   Don't configure PYTHONPATH <-- default settings
   Next >> Finish

<< result >>
myproject and myapp icon as folder


3. initialize project in Eclipse
Eclipse menu > Project > Properties > PyDev - PYTHONPATH > click "Add Source Folder"
   select "myproject" root folder

<< result >>
myproject and myapp icon as package

Eclipse PyDev Project Explorer > right-click package myapp > New > PyDev Package
   Source Folder = /myproject
   Name = myapp.models
repeat to create package myapp.views, myapp.utils


4. create database for project
   4.1 log in to MySQL as root
        mysql -h localhost -u root -p'rootpassword'
   4.2 create database with default character set and collation
        CREATE DATABASE mydb DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
   4.3 create user and grant privileges
        CREATE USER 'django_dbuser'@'%' IDENTIFIED BY 'djangodbpassword';
        GRANT ALL ON mydb.* to 'django_dbuser'@'%';
        CREATE USER 'django_dbuser'@'localhost' IDENTIFIED BY 'djangodbpassword';
        GRANT ALL ON mydb.* to 'django_dbuser'@'localhost';



5. modify DATABASES in settings.py for project database, e.g.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'mydb',            # Or path to database file if using sqlite3.
        # The following settings are not used with sqlite3:
        'USER': 'django_dbuser',
        'PASSWORD': 'djangodbpassword',
        'HOST': '',                      # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
        'PORT': '',                      # Set to empty string for default.
    }
}



6. modify INSTALLED_APPS in settings.py to use south
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'south',
)



7. syncdb
  python manage.py syncdb

วันจันทร์ที่ 21 ตุลาคม พ.ศ. 2556

Passing Request from AngularJS to Django

transform POST data before send from Angular to Django (with array of object)

// NOTE: this 2 lines for Django to receive POST data
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
$httpProvider.defaults.transformRequest = function(data) {
   if (data === undefined) {
      return data;
   }
   // NOTE: if transform this way -> at Django use json.loads(request.body)
   // can support array of object (no need to use getlist() at Django side)
   return JSON.stringify(data);
   // NOTE: if transform this way -> at Django use request.POST['key']
   // array of object will be sent with key like 'rows[10][id]', 'row[10][revenue]'
   // need to parse at Django side
// return $.param(data);
}

Passing Django's CSRF token through AngularJS

option 1:
var csrf_token = $('input[name="csrfmiddlewaretoken"]').val();
$http.post("/import_actual_simple_xls", postData, {
    headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', "X-CSRFToken" : csrf_token},
}).success(function(data, status, headers, config) {
    //other things to do when success

    ...
});

option 2:
ImportControllers.config(['$httpProvider', function($httpProvider) {
    // NOTE: this 2 lines for Django's csrf_token   

    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}]);

AngularJS - Any way for $http.post to send request parameters instead of JSON?

When post data using AngularJS' $http or $http.post(),
at Django side, request.POST is an empty dict but request.body has JSON string.

Use method in this page to transform request data JSON string --> request data parameter,
e.g.
{"param1":"value1","param2":"value2","param3":"value3"} --> param1=value1&param2=value2&param3=value3

Set up global transformRequest function:

var app = angular.module('myApp');

app.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return $.param(data);
    }
});

Sample non-global transformRequest per call:

var transform = function(data){
    return $.param(data);
}


$http.post("/foo/bar", requestData, {
    headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
    transformRequest: transform
}).success(function(responseData) {
    //do stuff with response

    ....});

For Hidden Field to Send Form Data

use <input name="stock_code" type="hidden" value="{{ actual.code }}">

Excel File Upload

After spending THE WHOLE DAY surfing internet, trying to find the way to send form data (including uploaded file) from AngularJS front-end to Django backend, here are what I get.
jQuery Form is my HERO.
My solution here is
- use jQuery Form to submit form data and file upload to backend.
- use dataType: 'html' for option passed to jQuery Form (I didn't know why but 'xml' didn't work).
- pass callback function to jQuery Form to update models when got response back.
- in the callback function, use $scope.$apply() to force AngularJS refresh its model after posted.
- no progress bar, no upload multiple files here because I don't need it now, but I really appreciate if you could guide me how to do.
NOTE: an example of upload progress here >> http://jquery.malsup.com/form/progress.html

html part (sample form which has both primitive data (multiplier) and file upload (input_excel))

<div id="div_load_xls" class="user-form col-lg-8" ng-app='myApp' ng-controller='importXlsController'>

    <form id="frm_load_xls" ng-submit='submitXls()' class="form-horizontal" method="post">

    {% csrf_token %}

        <input id="id_multiplier" name="multiplier" type="text" />

        <input id="id_input_excel" name="input_excel" type="file" />

        <input type="submit" name="validate_simple_actual" value="Validate" />

    </form>

</div>


Javascript part


<script src='/static/lib/js/angular-1.0.8.js'></script>    
<script src='/static/lib/js/jquery.form.js'></script>    

<script language="javascript" type="text/javascript">

    // get CSRF token from form's hidden field (generated from {% csfr_token %})

    var csrf_token = $('input[name="csrfmiddlewaretoken"]').val();

    // define AngularJS module and controller
    var myAppModule = angular.module('myApp', []);
    myAppModule.controller('importXlsController', importXlsController);

    // controller definition
    function importXlsController($scope, $http) {
        // function called when click submit button (ng-submit in the above form)
        $scope.submitXls = function() {

         // submit options
         var options = {
            dataType: 'html', // don't know why, but I used 'xml' and it didn't work
            url: '/import_actual_simple_xls', // to Django backend
            success: $scope.showResponse // callback function to process response defined below
         }

         // submit form data
         $('#frm_load_xls').ajaxSubmit(options);
      };

      // callback function to process response
      $scope.showResponse = function(responseText, statusText, xhr, $form) {
          // update model fields
          // use $apply to force AngularJS view to refresh
          $scope.$apply( function() {
               $scope.messages = jQuery.parseJSON(responseText);
         });
        };
    };

</script>


django backend (urls.py omitted)

def import_actual_simple_xls(request):
    // read primitive form data

    mul = request.POST.get('multiplier', None)

    if mul:
        messages.append('multiplier = %s' % mul)
    else:
        messages.append('No multiplier')

    // get file uploaded (handle error if needed)
    input_excel = request.FILES.get('input_excel', None)
    if not input_excel:
        messages.append('No Xls')
    else:
        book = xlrd.open_workbook(file_contents=input_excel.read())
        if ('Question List' in book.sheet_names()):
            messages.append('xls OK')
        else:
            messages.append('xls NG')
    return JSONResponse(messages)

วันศุกร์ที่ 30 สิงหาคม พ.ศ. 2556

Django Admin Site Customization

1) enable customized Admin class

from django.contrib import admin

class QuestionAdmin(admin.ModelAdmin):
  pass

admin.site.register(Question, QuestionAdmin)

2) customize fields to display at list page

class QuestionAdmin(admin.ModelAdmin):
    # fields to display at list page
    list_display = ('id', 'subject', )
 
    # at list page, fields that when click will open individual edit page
    # NOTE: when edit from list page, also call save_model()
    # if save_model() depends on custom fields in custom form (which not exists in list page),
    # one work around is to call super class method to handle model save
    # see save_model() below.

    list_display_links = ['question_html']
 
    # fields to show at filter side bar
    list_filter = ('status', 'subject', 'source', 'year')
 
    # fields that can edit from list page
    list_editable = ('solution', 'question_type', 'point', 'status', )
 
    # - fields used for search when click search button
    # - can search across relationship, e.g. subject__name
    # - by default, search for objects contain case-insensitive specified keyword
    #   (i.e. in MySQL : WHERE source ILIKE '%Ent%')
    # - for faster search performance
    #   + prefix field name with '=' to search exact match, e.g. WHERE year='2555'
    #   + prefix field name with '^' to match beginning of the field

    search_fields = ['subject__name', 'source', '=year', 'tags__name', 'solution_html']

    # specified fields that are read-only
    # (enhance performance when display individual edit page)

    readonly_fields = ['created_time', 'student', 'question']

    # field used as date, and display as link in list page that when click can filter by that date field
    date_hierarchy = 'created_time'

    # specify default order when display list page
    ordering = ['-id']

    def save_model(self, request, obj, form, change):
        # if not change from QuestionAdminForm, use default save_model()
        if not isinstance(form, QuestionAdminForm):
            super(QuestionAdmin,self).save_model(request, obj, form, change)
            return


Some sample screens below.


2.1) display link in list page

by using a function in list_display, which return url and set allow_tags = True. 

class ModelAdmin(admin.ModelAdmin):
    list_display = ('id', 'view_link')
    def view_link(self, obj):
            q = Question.objects.get(node_id=obj.id)
            return u'<a href="%s">%s</a>' % (q.get_absolute_url(),q.name)
    view_link.allow_tags = True

2.2) filter data shown in list page

by overriding queryset() as shown below.

def queryset(self, request):
qs = super(NodeAdmin, self).queryset(request)
return qs.filter(type__in=[2, 3, 4])

2.3) collapse list filter sidebar (with jQuery)

detail here >> https://gist.github.com/abyx/1017597

class QuestionAdmin(admin.ModelAdmin):
    list_filter = ['activity_flag']

    class Media:
            js = ['/static/js/list_filter_collapse.js' ] # path to JavaScript file

content of list_filter_collapse.js

(function($){
ListFilterCollapsePrototype = {
    bindToggle: function(){
        var that = this;
        this.$filterTitle.click(function(){
            that.$filterContent.slideToggle();
            that.$list.toggleClass('filtered');
        });
    },
    init: function(filterEl) {
        this.$filterTitle = $(filterEl).children('h2');
        this.$filterContent = $(filterEl).children('h3, ul');
        $(this.$filterTitle).css('cursor', 'pointer');
        this.$list = $('#changelist');
        this.bindToggle();
    }
}
function ListFilterCollapse(filterEl) {
    this.init(filterEl);
}
ListFilterCollapse.prototype = ListFilterCollapsePrototype;

$(document).ready(function(){
    $('#changelist-filter').each(function(){
        var collapser = new ListFilterCollapse(this);
    });
});
})(django.jQuery);


3) use inline to display/edit/add related object

# subclass from admin.TabularInline
class SectionResourceInline(admin.TabularInline):
    # model used in this inline
    model = SectionResource
    # fields display in this inline
    fields = ('type', 'seq_no', 'node')
    # specify if to let user input id instead of select from drop-down list
    # to improve performance

    raw_id_fields = ['node']
    # extra row displayed to add new data
    extra = 1

4.1) use custom form to display/edit individual object

from django.contrib import admin

class QuestionAdminForm(forms.ModelForm):
    ...

    class Meta:
        model = Question

class QuestionAdmin(admin.ModelAdmin):
    form = QuestionAdminForm
 
    # group fields in custom Form
    # 1st element in tuple = verbose name
    # 2nd element in tuple = dict with grouped fields, each inner tuple are fields in same line
    # e.g. group 'Information' has 2 lines,
    # first line has field difficulty, second line has source, year, seq_no
    # can specify custom fields together with fields in Model (e.g. new_chapter below)

    fieldsets = (
        ('Content', {'fields': (('subject', 'chapter', 'new_chapter'), ), }),
        ('Information', {'fields': ('difficulty',
                                    ('source', 'year', 'seq_no'),
                                   ),
                        }),
    )
    ...


Sample screen here.

4.2) keep original field value to use when save

in custom form : override __init__() to keep original field value somewhere.
class NodeAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(NodeAdminForm, self).__init__(*args, **kwargs)

        # initiate values for custom fields if change_form
        node = kwargs.pop('instance', None)
        if node:
            self.old_size = node.size

    class Meta:
        model = Node
in admin class : refer to original field value in form when save object
class NodeAdmin(admin.ModelAdmin):
    form = NodeAdminForm
    def save_model(self, request, obj, form, change):
        if form.old_size != obj.size:
            # do something
        super(NodeAdmin, self).save_model(request, obj, form, change)

4.3) use custom fields in Form

class QuestionAdminForm(forms.ModelForm):
    # queryset : to get available choices
    # widget : use admin.widgets.FilteredSelectMultiple to display 2 boxes : available choices <--> selected choices
    #          2nd parameter : True if display 2 boxes vertically, False if display horizontally
    #                          rows in attrs is approximate size of widget?

    internal_label = forms.ModelMultipleChoiceField(label='Internal Label', required=False,queryset=Label.objects.filter(type=Label.INTERNAL_LABEL_TYPE).order_by('name'),widget= admin.widgets.FilteredSelectMultiple('Internal Label', False, attrs={'rows':'10'})
                       
                       

    # show as dropdown widget (single select)
    # use ModelMultipleChoiceField for multiple select dropdown

    chapter = forms.ModelChoiceField(queryset=Chapter.objects.all().order_by('name'), required=False)

    def __init__(self, *args, **kwargs):
        # to display other fields in model
        super(QuestionAdminForm, self).__init__(*args, **kwargs)

        # set required fields
        self.fields['field_name'].required = True
        ...

        # use RelatedFieldWidgetWrapper to add green PLUS sign besides FilteredSelectMultiple widget
        # detail here >> http://dashdrum.com/blog/2012/07/relatedfieldwidgetwrapper/
        # 1st parameter = widget to be wrapped
        # 2nd parameter = a relation that defines the two models involved
        # 3rd parameter = a reference to the admin site (assigned from Admin class' __init__())
        #                 QuestionLabel is a class that has ForeignKey to both Question and Label
        self.fields['internal_label'].widget = 
admin.widgets.RelatedFieldWidgetWrapper(self.fields['internal_label'].widget,
QuestionLabel._meta.get_field('label').rel, self.admin_site)
    class Meta:
        # model related to Form
        model = Question

        # specify special widget for field
        # AdminFileWidget is a widget to load file

        widgets = {'question_html': admin.widgets.AdminFileWidget, }


class QuestionAdmin(admin.ModelAdmin):
    form = QuestionAdminForm

    def __init__(self, model, admin_site):
        super(QuestionAdmin,self).__init__(model, admin_site)
     
        # capture the admin_site
        # for use with RelatedFieldWidgetWrapper in QuestionAdminForm
        self.form.admin_site = admin_site


Sample screen here.


4.4) initiate default value in custom Form

class QuestionAdminForm(forms.ModelForm):
    ...

    def __init__(self, *args, **kwargs):
        ...

        # get Model object from kwargs
        question = kwargs.pop('instance', None)
        if question:
            # for simple fields, simply set value
            self.question_html = question.question_html

            # for single select choice field, simply set selected object id
            self.initial['chapter'] = question.get_chapter_id()

            # for multiple select choice field,
            # create a dict with key = selected object id, value = True,
            # then set as initial value

            selected_internal_labels = {}
            for r in Label.objects.filter(...):
                selected_internal_labels[r.id] = True
            self.initial['internal_label'] = selected_internal_labels

4.5) validate input data (before calling Admin class' save_model())

NOTE: method call sequence : ModelForm.clean() > Model.clean() > ModelAdmin.save_model() > Model.save()
so validation can occur at ModelForm.clean() or Model.clean()

class QuestionAdminForm(forms.ModelForm):
    ...

    def clean(self):
        # default validation first
        cleaned_data = super(QuestionAdminForm, self).clean()

        # custom validation follows
        # raise a ValidationError and error message will be displayed at top of edit Form
        # ValidationError raised in Model.clean() also displayed in the same way

        if some_condition:
            raise ValidationError('subject mismatch')

        # don't forget to return cleaned data
        return cleaned_data

4.6) save model from custom Form

NOTE: method call sequence : ModelForm.clean() > Model.clean() > ModelAdmin.save_model() > Model.save()
so setting default value can be done at ModelAdmin.save_model() or Model.save()

class QuestionAdmin(admin.ModelAdmin):
    ...

    def save_model(self, request, obj, form, change):
        # call super class' save_model() to handle changes from list page
        # (if we use custom form with custom fields not in list page)

        if not isinstance(form, QuestionAdminForm):
            super(QuestionAdmin,self).save_model(request, obj, form, change)
            return

        # obj = model object to be saved
        # can set default value from request
        # NOTE: use request.POST.get() to avoid exception when key not exists (i.e. not input from Form)
        # NOTE: when get a list from request object, use request.POST.getlist('a_key_to_list_value'),
        #       not simply call request.POST.get()

        obj.created_by_id = request.user.id

        # can set default value from form data
        obj.question_html = form.question_html

        # call super class' save_model() method for default process
        super(QuestionAdmin,self).save_model(request, obj, form, change)

        # then some special process follows
        ...

        # call message_user() to display some messages
        self.message_user(request, 'done...')


5) specific action in Admin Form

detail here >> https://docs.djangoproject.com/en/1.5/ref/contrib/admin/actions/

class QuestionAdmin(admin.ModelAdmin):
    actions = ['my_action']

    # queryset contain objects selected by user from list page
    def my_action(self, request, queryset):
        # for each selected object, do something
        for r in queryset:
            do_something(r)
     
        # show some message after completed
        self.message_user(request, "successfully do something")
    # a verbose description to show in page
    my_action.short_description = 'Do something for some purpose'

วันอังคารที่ 30 กรกฎาคม พ.ศ. 2556

Django Login Redirect

at login.html
<input type="hidden" name="next" value="{{ next }}" />


at urls.py
urlpatterns += patterns('',
    url(r'^login$', 'user_login'),
    ...
)


at method to process login (e.g. user_login())
def apu_login(request):     ...
    if request.method == "GET":
        return render(request, "login.html", {'next': request.GET.get('next', '/default_page')})
    ...
    return HttpResponseRedirect(request.POST.get('next', '/default_page'))



at method that require login, add decorator e.g. like below (require user to be staff)
from django.contrib.auth.decorators import user_passes_test
...
@user_passes_test(lambda u: u.is_staff)
def import_excel_view(request):

    ...

Django Sample Template 500 & 404

detail here >> http://david.feinzeig.com/blog/2012/02/18/tips-for-creating-404-page-not-found-and-500-server-error-templates-in-django-plus-configuring-email-alerts/

500.html (for server error)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
 <title>Page unavailable</title>
</head>

<body>
 <h1>Page unavailable</h1>

 <p>Sorry, but the requested page is unavailable due to a server hiccup.</p>

 <p>Our engineers have been notified, so check back later.</p>
</body>

</html>

404.html (for page not found)
{% extends "base.html" %}
{% load i18n %}

{% block title %}Page not found{% endblock %}

{% block content %}
<h1>Page not found</h1>

<p>Sorry, but the requested page could not be found.</p>
{% endblock %}

วันจันทร์ที่ 8 กรกฎาคม พ.ศ. 2556

Create Script to Run Django Method

Sample script to run Django method from command line.
Assume there is
- /path/to/my/project/settings.py for Django settings
- /path/to/my/project/mymodule/models/mymodel.py
- Django class myclass in the above mymodel.py with method mymethod()

#!/usr/bin/python2.7

import os
import sys

sys.path.append('/path/to/my/project/')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from mymodule.models.mymodel import myclass

myclass.mymethod()


Sample script that output result to a log file with date&time in file name as below (can use with Python 2.6+).
#!/usr/bin/python2.7
from __future__ import print_function
import os
import sys

sys.path.append('/path/to/my/project/')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from datetime import datetime
from mymodule.models.mymodel import myclass

t = datetime.now()
fname = '/path/to/log/folder/%s.log' % t.strftime('%Y-%m-%d_%H%M%S')
f = open(fname, 'w')
print('done some task with result = %s' % myclass.method(), file=f)
f.close()

a log will be written to file like /path/to/log/floder/2013-07-09_151201.log

วันศุกร์ที่ 5 กรกฎาคม พ.ศ. 2556

Excel in Python

detail here >> http://stackoverflow.com/questions/3504604/recommend-a-python-library-to-read-excel-xls-files
official document for xlrd here >> https://secure.simplistix.co.uk/svn/xlrd/trunk/xlrd/doc/xlrd.html?p=4966

1. install
    easy_install xlrd
    easy_install xlwt
    easy_install xlutils

2. register to Eclipse
  go to menu Window > Preferences > PyDev > Interpreter-Python
  click button 'New Folder' to add
  /usr/local/lib/python2.7/dist-packages/xlrd-0.9.2-py2.7.egg
  /usr/local/lib/python2.7/dist-packages/xlutils-1.6.0-py2.7.egg
  /usr/local/lib/python2.7/dist-packages/xlwt-0.7.5-py2.7.egg

3. sample code to read from Excel file
    import xlrd

    book = xlrd.open_workbook('myfile.xls')

    print book.nsheets
    print book.sheet_names()

    sh = book.sheet_by_index(0)
    print sh.name, sh.nrows, sh.ncols



วันพุธที่ 3 กรกฎาคม พ.ศ. 2556

Display String in ugettext_lazy

to display string in ugettext_lazy, use
u'%s' % ugettext_lazy('some text')

without "u", you will get something like
<django.utils.functional.__proxy__ object at 0x7fb214048650>
instead of text itself.

วันอังคารที่ 25 มิถุนายน พ.ศ. 2556

Created Timestamp and Updated Timestamp in Django

Added following fields in Django model for both timestamp
    created_time = models.DateTimeField(auto_now_add=True, blank=True)
    last_updated = models.DateTimeField(auto_now=True, blank=True)


NOTE: about timezone
There are 2 settings in settings.py
    TIME_ZONE = 'Asia/Bangkok'
    USE_TZ = False


if set like above, DateTimeField with auto_now and auto_now_add will use local time as indicated in TIME_ZONE, e.g. 17:00:00 (GMT+7:00)

however, if set USE_TZ = True, auto_now and auto_now_add will use UTC time, e.g. 10:00:00

Django ManytoMany Field

Sample model
class Chapter(models.Model):
    name = models.CharField(max_length=100)
    tags = models.ManyToManyField(Tag, blank=True, null=True)

Create Relationship - One by One
    new_chapter = Chapter.objects.create(name=chapter_name)
    new_tag = Tag.objects.create(name=tag_name)
    new_chapter.tags.add(new_tag)

Create Relationship - Multiple
    new_section = Section.objects.create(name=section_name, chapter=parent_chapter)
    new_section.tags = list(parent_chapter.tags)

Iterate ManyToMany relationship
    for tag in parent_chapter.tags.all():
        new_section.tags.add(tag)

วันเสาร์ที่ 15 มิถุนายน พ.ศ. 2556

Custom Field in Django with South Support

To define custom field in Django, here is an example:
detail here >> http://djangosnippets.org/snippets/2513/ and http://djangosnippets.org/snippets/1244/

class TinyIntegerField(models.SmallIntegerField):
    def db_type(self, connection):
        if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
            return "tinyint"
        else:
            return super(TinyIntegerField, self).db_type(connection)

    def get_internal_type(self):
        return "TinyIntegerField"

in this example, db_type() tell Django to use field type tinyint if backend is MySQL

Using this custom field type, when try to migrate with South, this error happened.
 ! South cannot introspect some fields; this is probably because they are custom
 ! fields. If they worked in 0.6 or below, this is because we have removed the
 ! models parser (it often broke things).
 ! To fix this, read http://south.aeracode.org/wiki/MyFieldsDontWork

To fix this problem, add a method south_field_triple in your custom field class
detail here >> http://south.readthedocs.org/en/latest/customfields.html

    def south_field_triple(self):
        from south.modelsinspector import introspector
        field_class = self.__class__.__module__ + '.' + self.__class__.__name__
        args, kwargs = introspector(self)
        return (field_class, args, kwargs)

How to Find Foreign Key Constraints in MySQL

Detail here >> http://stackoverflow.com/questions/806989/mysql-how-to-i-find-all-tables-that-have-foreign-keys-that-reference-particular

USE information_schema;
SELECT * FROM KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'X' AND REFERENCED_COLUMN_NAME = 'X_id';

วันจันทร์ที่ 20 พฤษภาคม พ.ศ. 2556

How to Use South in Django to Manage Database

Overview

South is a module to help managing database tables when you update models in Django.
Steps to start using South described below.

Described here is case with existing project + database just created + no table created yet
see detail for other cases here >> http://stackoverflow.com/questions/4840102/how-come-my-south-migrations-doesnt-work-for-django


Install South


detail here >> http://south.readthedocs.org/en/0.7.6/
  sudo easy_install South


Initiate South in Legacy Project + No Database

1. add South to INSTALLED_APPS in your settings.py file (not add your application at this step)
2. run python manage.py syncdb
    this will create South table south_migrationhistory in your database.
    skip this step, you will encounter error "Table 'app_name.south_migrationhistory' doesn't exist" when you run command python manage.py migrate app_name in step 5
3. now add your application to INSTALLED_APPS in your settings.py file
4. run python manage.py schemamigration app_name --initial
    this will create initial migration record
5. run python manage.py migrate app_name
If any error occur, you can reset South history as described below.

Reset South History 

detail here >> http://stackoverflow.com/questions/4625712/whats-the-recommended-approach-to-resetting-migration-history-using-django-sout
in case of any error, use following commands

  rm -r appname/migrations/ 
  python manage.py reset south << see NOTE below before run this command
  python manage.py convert_to_south appname 
then drop all related database tables, and initiate South again as described above.
NOTE:
- reset south will delete ALL migration history for all apps that you have installed.
  avoid using this if you only want to remove migration history of some specific app. selectively delete from table south_migrationhistory instead.
  e.g. delete from south_migrationhistory where app_name = 'homepage';
- last command, convert_to_south, use to avoid dropping all your tables. it is equivalent to these two commands
  python manage.py schemamigration --initial app_name_1
  python manage.py schemamigration --initial app_name_2
     ...
  python manage.py migrate --fake

Ongoing Maintenance

after modify models, run following commands
  python manage.py schemamigration app_name --auto
  python manage.py migrate app_name




วันเสาร์ที่ 4 พฤษภาคม พ.ศ. 2556

Python Regular Expression Cheat Sheet


Meta-Character

[a-c], [abc] : a, b, c
[^5] : all chars except 5
\d : [0-9] decimal digit
\D : [^0-9] non-digit chars
\s : [ \t\n\r\f\v] whitespace chars
\S : [^ \t\n\r\f\v] non-whitespace chars
\w : [a-zA-Z0-9_] alphanumeric chars
\W : [^a-zA-Z0-9_] non-alphanumeric chars
^ : beginning of line
\A : beginning of string (differ from ^ for multi-line string)
$ : end of line
\Z : end of string (differ from ^ for multi-line string)
\b : word boundary
\B : non-word boundary
() : group e.g. (ab)+ match ab, abab, ababab, ...
\1, \2, ... : reference to group 1, group 2, ...
(?P<name>...) : grouped name, e.g. (?P<word>\b\w+\b) : matched group (\b\w+\b) will be named word

Repeating

* : 0+ repeating (greedy repeating : get as much as it could)
+ : 1+ repeating (greedy repeating : get as much as it could)
? : 0..1 repeating = {0,1} (greedy repeating : get as much as it could)
*?, +?, ?? : same as above but non-greedy repeating
{m} : exactly repeating m times
{m,n} : m..n repeating
{,n} : 0..n repeating
{m,} : m.. repeating

Usage - Pattern object

compile() : compile pattern string to Pattern object
match() : matches at the beginning of the string, return Match object or None if not match
search() : matches at any location of the string, return Match object or None if not match
findall() : return all matched substrings as a list
finditer() : return all matched substrings as an iterator
Usage - Module level
re.match(<regex string>, <target string>) :
re.search(...)
re.findall(...)
re.finditer(...)

Usage - Match object

group() : return matched string
start() : return starting position (0-indexed)
end() : return ending position (0-indexed, excluded)
span() return tuple of (start, end) positions
match length = end() - start()

Sample

>>> import re
>>> p = re.compile(’[a-z]+’)
>>> p.match("")
>>> print p.match("")
None
>>> m = p.match(’tempo’)
>>> m.group()
’tempo’
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)
>>>
>>>
>>> m = p.search(’::: message’)
>>> m.group()
’message’
>>> m.span()
(4, 11)
>>>
>>>
>>> p = re.compile(’\d+’)
>>> p.findall(’12 drummers drumming, 11 pipers piping, 10 lords a-leaping’)
[’12’, ’11’, ’10’]
>>> iterator = p.finditer(’12 drummers drumming, 11 ... 10 ...’)
>>> for match in iterator:
...     print match.span()
...
(0, 2)
(22, 24)
(29, 31)
>>>
>>>
>>> p = re.compile(r’(?P<word>\b\w+\b)’)
>>> m = p.search( ’(((( Lots of punctuation )))’ )
>>> m.group(’word’)
’Lots’
>>> m.group(1)
’Lots’
>>>
>>>
>>> p = re.compile(r’\W+’)
>>> p2 = re.compile(r’(\W+)’)
>>> p.split(’This... is a test.’) # delimiter not included
[’This’, ’is’, ’a’, ’test’, ’’]
>>> p2.split(’This... is a test.’) # delimiter included
[’This’, ’... ’, ’is’, ’ ’, ’a’, ’ ’, ’test’, ’.’, ’’]
>>>
>>>
>>> p = re.compile( ’(blue|white|red)’)
>>> p.sub( ’colour’, ’blue socks and red shoes’)
’colour socks and colour shoes’
>>> p.sub( ’colour’, ’blue socks and red shoes’, count=1)
’colour socks and red shoes’
>>>
>>>
>>> s = ’<html><head><title>Title</title>’
>>> len(s)
32
>>> print re.match(’<.*>’, s).span()
(0, 32)
>>> print re.match(’<.*>’, s).group() # greedy match
<html><head><title>Title</title>
>>> print re.match(’<.*?>’, s).group() # non-greedy match, others are *?, +?, ??, or {m,n}?
<html>

วันพุธที่ 1 พฤษภาคม พ.ศ. 2556

Django Model Cheat Sheet

Field Types + Specific Options

<< boolean >>
- BooleanField << True/False
- NullBooleanField << null/True/False

<< number >>
- IntegerField
- SmallIntegerField
- PositiveIntegerField
- PositiveSmallIntegerField
- BigIntegerField << 64-bit integer
- AutoField << auto-incremental IntegerField
- DecimalField
  max_digits, decimal_places << 999.99 : max_digit=5, decimal_places=2
- FloatField

<< text >>
- CharField
  max_length << length in characters
- TextField
- SlugField
- CommaSeparatedIntegerField
- URLField
  verify_exists
- EmailField
  max_length=75
- IPAddressField
- GenericIPAddressField

<< date/time >>
- DateField, DateTimeField, TimeField
  auto_now=False << set current timestamp for every save
  auto_now_add=False << set current timestamp when first create

<< file >>
- FileField << file upload field
  upload_to << path appended to MEDIA_ROOT; can be callable object; can use strftime() formatting
  max_length=100
- ImageField << subclass from FileField
  height_field, width_field << auto-set
- FilePathField

<< relationship >>
- ForeignKey(one-sided model) << many-to-one relationship
  limit_choices_to << effect Admin/ModelForm choices
    e.g. limit_choices_to = {’pub_date__lte’: datetime.now}
  related_name << backward relationship (1 --> many)
  to_field
  on_delete=models.CASCADE << emulate ON DELETE CASCADE behavior
    e.g. user = models.ForeignKey(on_delete=models.SET_NULL) << when delete parent, set reference to NULL
    CASCADE = also delete child when parent is deleted
    PROTECT = cannot delete parent if child exists
    SET_NULL
    SET_DEFAULT = set to default value if parent deleted
    SET()
    DO_NOTHING
- ManyToManyField(other model) << many-to-many relationship
  related_name << same as ForeignKey
  limit_choices_to << same as ForeignKey
  symmetrical << for self-reference
  through << intermidiary model (for additional data)
  db_table << table to store many-to-many relationship
- OneToOneField << one-to-one relationship
  parent_link=False << True=link back to parent class (in subclass model)

(General) Field Options

- null=False << allow null in database
- blank=False << for validation
- default << default value
- choices << value stored + value displayed
    YEAR_IN_SCHOOL_CHOICES = (
        (’FR’, ’Freshman’),
        (’SO’, ’Sophomore’),
        (’JR’, ’Junior’),
        (’SR’, ’Senior’),
        (’GR’, ’Graduate’),
    )
    MEDIA_CHOICES = (
        (’Audio’, (
            (’vinyl’, ’Vinyl’),
            (’cd’, ’CD’),
        )),
        (’Video’, (
            (’vhs’, ’VHS Tape’),
            (’dvd’, ’DVD’),
        )),
        (’unknown’, ’Unknown’),
    )
- db_column << column name in database (override auto-generated column name)
- db_index << True = create index for this field
- editable=True << False = not displayed for edit in form
- unique << True = unique value in field
  unique_for_date, unique_for_month, unique_for_year
- primary_key << True = primary key for model (override auto-generated PK field)
- help_text << display with form widget
- verbose_name << human-readable name

Meta Options

- verbose_name, verbose_name_plural
  human-readable name for model
- unique_together
  combinated unique fields, e.g. unique_together = (("driver", "restaurant"),)
- abstract
  True=model for abstract class
- db_table
  database table used for model
- get_latest_by
  DateField/DateTimeField used to identify latest data (use in manager's latest() method)
- ordering
  default order when get, e.g. ordering = [’-order_date’] << order by order_date descending
- order_with_respect_to
  True=children has order related to its parent (add additional column suffixed _order)
  set at child model;
  parent model can get ordered children using get_<RELATED>_order()
  parent model can set children order using set_<RELATED>_order()
  child model can use get_next_in_order(), get_previous_in_order()
- app_label
  use when model exists outside standard models.py