Page 2 of 4

Automatic admin interface for SQL Server using Django

A problem that we face everyday is creating CRUD interfaced for our databases. Creating a CRUD interface is always a boring and tedious job. Even if you want to initialize your data, you may break something by mistake if you use SQL Server Management Studio, because it doesn't know anything about your business rules. Django_ - A web framework for Python_ - gives you the solution. It has an application called "Django Admin" that provides a customizable, production-ready, nice-looking UI on top of your DB. If you want to see the final results, see the screenshots at the end of the post.

Setting the environment

  1. Install Python 2.5 or newer.
  2. Install Django 1.0 or newer.
  3. Download and install pyodbc. You should install the one that is compatible with your version of Python, e.g. if you installed Python 2.5, you should select pyodbc-2.0.58.win32-py2.5.exe.
  4. Checkout django-pyodbc from http://django-pyodbc.googlecode.com/svn/trunk/ using a Subversion client, then you should move the directory sql_server to C:\Python25\Lib\site-packages\.

Creating the Django project

A Django application is a complete component, including the DB model, views and templates. Good examples of Django application are user registration, tagging, search, etc. You can find a lot of reusable applications on http://www.djangopackages.com/. A Django project is a group of application that works together. Now you have the environment setup for Django. The next step is creating a new Django project. On the console, write this command

C:\Python25\Lib\site-packages\django\bin\django-admin.py startproject sqlserveradmin

Then create an application using this command:

manage.py startapp mydb

To add the application to the project. You must change INSTALLED_APPS in settings.py to include the app.

INSTALLED_APPS = (    
   'django.contrib.auth',     
   'django.contrib.contenttypes',    
   'django.contrib.sessions',     
   'django.contrib.sites',     
   'mydb',
)

Setting the database

Now you have the project in place. You need to modify settings.py to point to your DB. Change these fields to be

DATABASE_ENGINE = 'sql_server.pyodbc' 
DATABASE_ODBC_DRIVER = 'SQL Native Client'
DATABASE_NAME = 'db_name' 
DATABASE_USER = 'webapp' 
DATABASE_PASSWORD = 'sikrit' 
DATABASE_HOST = r'test_server\SQLEXPRESS'

Generating DB models

On the console, run the following command

manage.py inspectdb > inspected_models.py

This will generate Python models for your tables. Unfortunately, they are not arranged. To arrange them, use the script that I posted to http://www.djangosnippets.org/snippets/1203/.

rearrange_models.py inspected_models.py models.py

Use the new models.py to replace the old one in the folder mydb.

Installing the admin

To install the admin, you need to add the following line to INSTALLED_APPS in settings.py. It should look like

INSTALLED_APPS = (
    'django.contrib.auth',    
    'django.contrib.contenttypes',     
    'django.contrib.sessions',    
    'django.contrib.sites',     
    'django.contrib.admin', 
    'mydb',
)

then run this command to add the tables necessary for the admin to work.

manage.py syncdb

It will ask you to add a superuser. This superuser will allow you to access the admin.

You need to tell Django what models that you need to be viewed in the admin. For our case, we want to view all the tables. I will use a little bit of Python magic to add all the models to be viewed in the admin. Put the following code in a file called admin.py in your application folder.

from django.contrib import admin 
from django.db.models import Model 
import models # This is your models' module 
for m in dir(models):
   class_ = getattr(models, m, None)
   if class_ and isinstance(class_, type) and issubclass(class_,Model):
      admin.site.register(class_)

Congratulations. We are done.

What you can do more

  1. Change the templates used to view the UI.
  2. Customize the names of the models and fields, for example you can supply correct pluralization.
  3. For enums, you can specify the available choices, and the admin will view it as a drop down.
  4. You can add more users to the admin, and specify their permissions.
  5. And a lot more. See http://docs.djangoproject.com/en/dev/

I hope this wets your appetite for learning Django.

Screenshots

The home page. I had to remove everything that reveals the application. As you can see, Django admin also stores a history of changes.

1

This is the view for students. I customized it a little bit to view students' emails, but you can customize it more.

2

This is the form to change the student. The "UserId" is a foreign key to the "Users" table. Django admin is smart enough to grab them for your. If you want to add a new user, you can add it by clicking the small "+" sign beside the drop down list.

3

This shows the screen for users in the "Auth" application (which is added by Django itself). This shows a more advanced listing: you can select the columns that will be displayed, with sorting. You can add filters (on the right), and you can add search also.

4

This is the form for editing users.

5

Python Arabic Book

A friend told me that there is an arabic book for Python, which is a translation of "Byte of Python", one of the most popular Python books. This is a good step toward popularizing Python, specially among children. Please go and download the arabic version. Read it, and tell the author about any enhancements that the book needs.

Help spread Firefox

https://affiliates.mozilla.org/

Stupid Type Systems - Innecessary Casting in C#

If List<> inherits IList<>, and MyIdentity inherits IIdentity, then why the heck the C# compiler cannot cast from List<MyIdentity> to IList<IIdentity>?????? It is the same, the compiler is not smart enough to know this. So to satisfy the compiler - acting as a good compiler slave - I have to write this

IList<IIdentity> returnList = new List<IIdentity>();
// A variable name cannot be stupider
foreach (var item in originalList)
{
    returnList.Add(item);
}
return returnList;

And people were wondering why dynamic languages takes less lines of code.

C#.Next, What it must NOT include?

There are some talks lately about the next version of C#, and what is should add. People has gone too far asking for new features, most notably the dynamic lookup

static void Main(string[] args)
{
    dynamic
    {
        object myDynamicObject = GetDynamicObject();
        myDynamicObject.SomeMethod();
        // call a method
        myDynamicObject.someString = "value";
        // Set a field
        myDynamicObject[0] = 25;
        // Access an indexer
    }
}

The above looks ridiculous to me. C# is statically-typed language, and it should stay like this. Trying to add dynamic typing to it - even if it was optional - is stupid. For me, a language must establish a few basic concept and stick to them, not try to satisfy every one out there. C# has incomplete features. These should be completed first before adding any new features. Take type inference as an example. You cannot return anonymous types from methods, because you don't know their names. It should allow something like

public anonymous MyMethod(string email)
{
    return from user in Users
    where user.Email = email
    select new { FullName = user.FirstName + " " + user.LastName };
}

It can also add named parameters, and default values for them, just like Python. It can be supported indirectly now by passing anonymous types, but complete support for them would be better. Dynamic languages are not just about dynamic lookup, and supporting broken dynamic lookup - just like the above mentioned example - is going to be, really, broken. What about dynamically adding new methods? What about using making a class as a proxy, say, for a web service? What about object-specific members - members which exists for a specific member? It can get very complex, and the only known way to allow dynamic features is to make your language dynamic. The beauty of the CLR, is allowing different languages to run and inter-operate. Unfortunately, it was designed with static typing in mind, which appears clearly in the BCL design. I wish the DLR team has these limitations in mind so they address them better, specially the importance of providing libraries which fits the dynamic languages way of doing things. One language is not enough, and one language which tries to fit all purposes is going to be very complex - just like C++. The solution is learning different languages and using the appropriate one when it fits, and integrating them when you need to.

RSS feeds for youtube

Everyone uses youtube to see his favorite videos. Most of the time, you need to subscribe to a certain user’s feed using RSS. Unfortunately, the 'Subscribe' buttons subscribes using their own notification system, and you have to be a member to subscribe.

Contrary to popular beliefs, youtube provides RSS feeds, but they are hidden. You can subscribe to a user's feed using

http://gdata.youtube.com/feeds/base/users/mtayseer/uploads

And to a certain tag using

http://gdata.youtube.com/feeds/base/videos/-/egypt

The question is: why did they hide this?

The Django book is released

Finally, the long-awaited Django book is released under the GNU Free Documentation license. Everybody can learn and suggest enhancements to the book or Django. Does Django runs with IronPython? Yes, it runs on IPCE, but I don't think that everything runs well, because the different DBMS drivers are not ported to IronPython yet.

Open Komodo is released

ActiveState, the leader in dynamic languages development tools, has released the OpenKomodo intiative. OpenKomodo is targeted at creating an open source IDE for dynamic languages and the web, based on ActiveState Komodo IDE (which is proprietary).

The First IronPython Book

Manning Publishing is working on the first book about IronPython, called IronPython in Action. You can download the first chapter for free. They have provided the chapters 2, 3 and 4 under their early access program.

Blame yourself before anybody else

Here at Silverkey, we use LLBLGen Pro as our ORM. One of the nicest features of it is prefetching related entities, so if you have a "CompanyEntity", you can tell it to fetch all related "Employee Entity". This makes our jobs easier, except when it doesn't!!! Today I had a few bugs with LLBL: Trying to save an entity threw NullReferenceException deeply from inside LLBL code. Even though it worked in some circumstances, it didn't work on others. It was easy to blame LLBL, since it was the source of the exception. So instead of letting LLBL load the related entities, I loaded them myself. This worked OK for sometime, but it started to through the same exception again. So it really appeard that the problem was not what I thought. First, I recognized two things

  1. It happens in with a certain class, so this class was the source of the problem
  2. It happens only when I changed the objects. If I didn't change them, it works OK.

The code for saving was like this

using (Transaction transaction = new Transaction(System.Data.IsolationLevel.ReadUncommitted, "SaveCompany"))
{
    try
    {
        // Save related entities
        foreach (Employee employee in CompanyEmployees)
        {
            employee.Transaction = transaction;
            employee.Save();
        }

        // Save modified fields
        _entity.Transaction = transaction;
        _entity.Save();

        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
 }

Trying to access any member of the entity now will throw a NullReferenceException. Though the entity is not null, it will try to connect to the database using the connection of the closed transaction, which will throw the NullReferenceException. It should be like this

using (Transaction transaction = new Transaction(System.Data.IsolationLevel.ReadUncommitted, "SaveCompany"))
{
    try
    {
        // Save related entities
        foreach (Employee employee in CompanyEmployees)
        {
            employee.Transaction = transaction;
            employee.Save();
        }

        // Save modified fields
        _entity.Transaction = transaction;
        _entity.Save();

        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
    finally
    {
        _entity.Transaction = null;
    }
 }

The lessons learnt: It's easy to blame the tools you use, but probably it is your fault

© 2014 Mohammad Tayseer

Theme by Anders NorenUp ↑