Saturday, May 28, 2011

Dynamic Initial Value for Django Admin Inline Form

I am impressed with Django Admin Inline Form. Even more impressed by the simplicity to set the initial value for it.

One can easily set the initial value for Django Admin Add Form, via url get parameter. For example: http://localhost:8080/admin/myapp/mytable/?myfield_id=1

This will not work if myfield is within the inline form, instead of regular form. To do that, override admin.TabularInline formfield_for_foreignkey method.

Here is an example:

class MyTabInline(admin.TabularInline):
    model = models.MyModel
    extra = 1

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'myfield':
            kwargs['initial'] = request.GET.get('myfield_id', '')
        return super(MyTabInline, self). 
                formfield_for_foreignkey(db_field, request, **kwargs)
Pretty simple.

Friday, May 27, 2011

Perform Extra Operations after Saving All Inlines in Django Admin

Django Admin provides quite solid foundation for performing CRUD operations. Djando Admin does most things already.
What I needed to do this time is to perform extra operations after saving all inlines in Django Admin. If I do not need to access request object, I could perform extra operations by overriding save method. The thing is I need to get current logged in user, that is exactly why I need to do it from admin.ModelAdmin.

First attempt is by overriding admin.ModelAdmin save_model method. Failed, due to after performing obj.save(), the inline data are not yet available.

What next? Fortunately, I found helpful information from this link.

From that, we could intercept right before preparing the response.
There are three methods which related with setting up response:
response_add, response_change, response_action.

Here is an example of overriding response_add method.
def response_add(self, request, new_object):
    # Perform extra operation after all inlines are saved
    return super(AdminSubClass, self).response_add(request, new_object)

There is a ticket with this issue.
Hopefully there will be even better way to perform such action.

Friday, May 20, 2011

Remove First N bytes From a (Binary) File

Situation:
You have a (binary) file, and you need to remove the first n bytes of data in it.

Solution:
Use dd. Read more about it, here.

Here is how to do it:
$ dd if=input_file ibs=1 skip=N of=output_file

N is the number of bytes to be removed.

example:
$ dd if=input_file.dat ibs=1 skip=3 obs=10M of=output_file.dat


Now, what if you need to remove the first N bytes and the last M bytes from a file?
$ dd if=input_file.dat ibs=1 skip=N count=X of=output_file.dat

To calculate X:
X = acutal_file_size_in_bytes - N - M


I recommend you to read through the man pages, and play around with ibs, obs, bs, count values and other parameters that might be useful.

Thursday, May 19, 2011

Python ValueError Bad Marshal Data

I encountered this problem when I copied my django project along with (cherrypy's) wsgiserver and started it.

To solve this issue, I needed to remove wsgiserver.pyc, and re-run the program. Just like that.

So, it is a good idea to clean all the *.pyc's, when moving/copying codes in a different computer, and let python recreated them all.

Serving Django with CherryPy wsgiserver

For my current project, I do not need heavy duty webserver. What I needed was a simple (python) webserver and cherrypy wsgiserver does the job.

To make things simple:
copy cherrypy/wsgiserver/__init__.py into the project folder and rename it as wsgiserver.py.

Next step is to create a wsgiserver startup script. It would be something like the following:

1:  import wsgiserver  
2:  import sys  
3:  import os  
4:  import django.core.handlers.wsgi  
5:  if __name__ == "__main__":  
6:    sys.path.append(os.path.realpath(os.path.dirname(__file__)))  # add django project absolute path  
7:    # Startup Django  
8:    os.environ['DJANGO_SETTINGS_MODULE'] = 'django_project_dir_name.settings'  
9:    server = wsgiserver.CherryPyWSGIServer(  
10:      ('127.0.0.1', 8080),  
11:      django.core.handlers.wsgi.WSGIHandler()  
12:    )  
13:    try:  
14:      server.start()  
15:    except KeyboardInterrupt:  
16:      print 'Stopping'  
17:      server.stop()  


Of course the performance is below apache, lighttpd, nginx, but when one does not need high performance and would like a simple (wsgi) webserver, this is one of the answers.

Thursday, May 5, 2011

TG2.1 + Eclipse + Pydev

Here's how you do it.

create launch_tg.py, in the same directory as development.ini, contains:
#################################
from paste.script.serve import ServeCommand
ServeCommand("serve").run(["development.ini"])
#################################

Start Eclipse.
File -> New -> Project -> Pydev -> Pydev Project
Project name: test
Click: "Please configure an interpreter in the related preferences before proceeding."
New -> Browse -> /your_virtualenv_dir/your_webapp_dir/bin/python -> Ok -> Ok
Ok -> Finish

Pydev Package Explorer -> right-click on 'test' -> Refresh
Run -> Debug Configurations -> Python Run (double click)
Main tab -> Project -> test
Main tab -> Main module -> ${workspace_loc:your_eclipse_project/src/your_webapp_dir/launch_tg.py}
Arguments tab -> Working directory -> ${workspace_loc:test/src/Test}
Click debug


Taken from: http://ping.fm/DnR7T

TG2.1 Trouble with Sqlite Database

If you use sqlite as the backend db for your tg2.1 web apps, you might occasionally get errors like the following:
ProgrammingError: (ProgrammingError) Cannot operate on a closed database

It turns out that the default sqlalchemy dbpool is not optimized for filebased db, like sqlite.

To solve this matter, simply change sqlalchemy dbpool to NullPool.

Make the modification in webapp/config/app_cfg.py:
#################################
from sqlalchemy.pool import NullPool
class PLAppConfig(AppConfig):
# override
def setup_sqlalchemy(self):
from sqlalchemy import engine_from_config
engine = engine_from_config(pylons_config, 'sqlalchemy.', poolclass=NullPool)
config['pylons.app_globals'].sa_engine = engine
# Pass the engine to initmodel, to be able to introspect tables
self.package.model.init_model(engine)

base_config = PLAppConfig()
#################################

All set, no more ProgrammingError.

TG2.1 Trouble with One to Many Relationship

Finally, I figured out what causes the 'ModelController' error in the TG2.1 Admin System Page. Thanks to Eclipse (Aptana Studio 3) and PyDev, I was able to debug and spot the cause of the trouble.
I was kind of surprised that Sprox, the module that is responsible for form-model binding, could not handle unique constraint in one to many relationship.
Due to this, all other kind of constraints on relationship will probably fail too. Perhaps UniqueConstraint also get effected.
So, mainly all other constraints that are not applied directly to the fields of the sqlalchemy models could not be handled properly by TG2.1 (sprox).

Since, unique constraint is very common in DB design, I really hope that this functionality could be handled not just in simple field but also in relationship and multi-columns unique contraint.

As for the 'elegant' solution, I have not yet find a way to do it. Perhaps, I could add validator in the relationship field/attribute (in sqlalchemy declarative model class), but no clue on how to implement it. Another thing is that even if I could do that, I am not sure it would work, since I am not even sure if relationship field would be registered to be checked by the framework.

It would be a good thing if in sprox the relationship field and the foreignkey field both are being checked as one.

As for now, I just need to do a work around to prevent adding duplicate one to many relationship.

Pretty impressed with TG2.1. This is by far the most flexible web framework that I have worked with. My suggestion to those who would use this framework is, be prepared to swim and dive in the source codes, as you definitely need to extend functionalities by overriding methods and sub-classing.