Java Jester

Monday, November 10, 2008

Custom Python Decorator

Decorators allow developers to use aspect oriented approach. Here is an example of a custom python/django decorator that checks if the user is a super user and redirects it to login page if not

def super_user(func):
    def call(request, *args, **kwds):
        user = request.user
        if user.is_authenticated() and user.is_superuser:
            return func(request, *args, **kwds)
        else:
            return utils.redirect(settings.LOGIN_URL)
    return call
Here is an example usage:
@super_user
def my_view(request):
    pass

 

Saturday, November 08, 2008

Django Tip: Cache List instead of QuerySet to minimize queries to the database

While playing with django debug toolbar, I noticed that some page on my application causes serious amount of queries that is being executed. One page actually executes around 580 queries so I investigated and noticed that this was caused probably by lazy initialization, not using select_related() on my queries and i'm caching QuerySet object instead of list.

An example of a getter function on my model:

@property
    def data(self):
        cache_data = getattr(self, 'cache_data', None)
        if cache_data:
            return cache_data
        else:
            self.cache_data = TimecardData.objects.filter(timecard_day=self)
            return self.cache_data

The code above retrieves all the associated data on my timecard but to prevent it from hitting the database everytime i call that data() method, i need to cache it. One mistake I did is to cache the returned query set instead of casting it to a list type because whenever the cache_data is retrieved, It will again hit the database since it's only a query set.

After refactoring the above code to this one:
@property
    def data(self):
        cache_data = getattr(self, 'cache_data', None)
        if cache_data:
            return cache_data
        else:
            self.cache_data = list(TimecardData.objects.select_related()
.filter(timecard_day=self))
            return self.cache_data
The queries was reduced from 560 to 300 queries.

Further looking at the problem, I refactored the code to this one:

I still have a long way debugging the problem but I hope this helps you aswell.
@property
    def data(self):
        cache_data = getattr(self, 'cache_data', None)
        if cache_data != None:
            return cache_data
        else:
            self.cache_data = list(TimecardData.objects.select_related().filter(timecard_day=self))
            return self.cache_data
The code above to this one checks using "if cache_data" but since empty list returns false if evaluated, it queries the database again instead of using cache. This one brought the queries from 580 to 120 (a very big difference)

 

Saturday, November 01, 2008

Profiling Django Applications

While working on one of my projects today, I noticed that the page loads slow. And the thing that helped me debug the problem was a tool called django toolbar. It's a django application that displays the time of request, the queries that were executed, cache and other information that you can use to optimize your application.

 

Saturday, October 18, 2008

Host Python Applications Better With mod_wsgi

As I was browsing, I came across an article used to host python web applications better. The module called 'mod_wsgi' is a new&better apache module than mod_python when hosting django/python apps. I haven't personally tried it (maybe I will this coming weekend) but the comments by other people are very interesting. Anyway to give you some short round-up, this are the things that it can do:

  • Can serve dynamic content by about 100-150% faster compare to mod_python
  • Consumes less memory
  • Secure (Don't know yet why it's secure)

Links:

http://code.google.com/p/modwsgi/

http://www.technobabble.dk/2008/aug/25/django-mod-wsgi-perfect-match/

http://code.djangoproject.com/wiki/django_apache_and_mod_wsgi

 

Saturday, October 18, 2008

Base Class For My Entities

Here is the base class of my entities. This one supports deleting of record by marking instead of permanently deleting the record:

class BaseModel(models.Model):
    """
    Base model for all entities
    """
   
    date_created = models.DateTimeField(auto_now_add=True)
    date_updated = models.DateTimeField(auto_now=True, auto_now_add=True)

    def delete(self, permanent=False):
        """
        Delete a model by flagging. An optional parameter 'permanent' can be passed to permanently delete it
        """
        if BaseModel.__deletable_type(self.__class__) and not permanent:
            self.deleted = 1
            self.save()
        else:
            models.Model.delete(self)
   
    @classmethod
    def all(cls, include_deleted=False):   
        """
        Get all query object with deleted records filtered
        """    
        deleted_field = hasattr(cls, "deleted")
        if BaseModel.__deletable_type(cls) and not include_deleted:
            query = cls.objects.all()
            return query.filter(deleted=False)
        else:
            return cls.objects.all()
   
    @classmethod
    def __deletable_type(cls, input_cls):
        """
        Check if this class is deletable by flagging. The algorithm is slow so it needs some tweaking later
        """
        for b in input_cls.__bases__:
            if b.__name__ == 'BaseDeletableUserModel': return True
        return False
   
    class Meta:
        abstract = True


class BaseDeletableModel(BaseModel):
    """
    A deletable base model
    """
   
    deleted = models.BooleanField(default=False)
       
    class Meta:
        abstract = True

The code above still needs to be improved so any kind of comments will be appreciated.

 


recommended books
Jester Cap Logo by Vonn reyjexter.com | Valid XHTML & CSS | 2008 | Design & Devt. by: vonnhugo

powered by:

django framework python google app engine