Djangosaur
django manage.py loaddata without constraints

I had to quickly inject a dumpdata json file on a mysql server and got this nice message:

IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails

I created a settings file for the loaddata command without mysql foreignkey checks.

DATABASES = { 
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
             "init_command": "SET foreign_key_checks = 0;",
        }        
    }       
}       
export DJANGO_SETTINGS_MODULE="project.settings_without_fk_ckecks"
python manage.py loaddata data.json
Django thumbnails, resize image with PIL and python

There is a wide variety of django filters and tags to crop, resize or make a thumbnail out of an image but most of them seem to reinvent the wheel.

How to resize, crop or create a thumbnail with python and PIL

You need to install the Python Image Library and this snake image to run this example.

#!/usr/bin/env python
try:
    from PIL import Image, ImageOps
except ImportError:
    import Image
    import ImageOps


image = Image.open('snake.jpg')

# ImageOps compatible mode
if image.mode not in ("L", "RGB"):
    image = image.convert("RGB")


imageresize = image.resize((200,200), Image.ANTIALIAS)
imageresize.save('resize_200_200_aa.jpg', 'JPEG', quality=75)

image.thumbnail((200,200), Image.ANTIALIAS)
image.save('thumbnail_200_200_aa.jpg', 'JPEG', quality=75)

imagefit = ImageOps.fit(image, (200, 200), Image.ANTIALIAS)
imagefit.save('fit_200_200_aa.jpg', 'JPEG', quality=75)

Image.resize

image resize

resize changes the aspect ratio of the image.

Image.thumbnail

image thumbnail

thumbnail keeps the aspect ratio of the image and scales it to the defined area.

Imageops.fit

image imageops fit

fit keeps the aspect ratio and crops the image to the size of the defined area.

Conclusion

We want our images to look nice so we will discard the resize function and implement thumbnail and fit inside django filters.

Django filters to scale and crop images

Our filter will receive an imagefield object (with path and url properties) and dimensions. It will check if the image has already been generated and serve it, otherwise, it will create a new resized image.

# my_apps/image/templatetags/image_tags.py
import os.path

from django import template

FMT = 'JPEG'
EXT = 'jpg'
QUAL = 75

register = template.Library()


def resized_path(path, size, method):
    "Returns the path for the resized image."

    dir, name = os.path.split(path)
    image_name, ext = name.rsplit('.', 1)
    return os.path.join(dir, '%s_%s_%s.%s' % (image_name, method, size, EXT))


def scale(imagefield, size, method='scale'):
    """ 
    Template filter used to scale an image
    that will fit inside the defined area.

    Returns the url of the resized image.

    {% load image_tags %}
    {{ profile.picture|scale:"48x48" }}
    """

    # imagefield can be a dict with "path" and "url" keys
    if imagefield.__class__.__name__ == 'dict':
        imagefield = type('imageobj', (object,), imagefield)

    image_path = resized_path(imagefield.path, size, method)

    if not os.path.exists(image_path):
        try:
            import Image
        except ImportError:
            try:
                from PIL import Image
            except ImportError:
                raise ImportError('Cannot import the Python Image Library.')

        image = Image.open(imagefield.path)

        # normalize image mode
        if image.mode != 'RGB':
            image = image.convert('RGB')

        # parse size string 'WIDTHxHEIGHT'
        width, height = [int(i) for i in size.split('x')]

        # use PIL methods to edit images
        if method == 'scale':
            image.thumbnail((width, height), Image.ANTIALIAS)
            image.save(image_path, FMT, quality=QUAL)

        elif method == 'crop':
            try:
                import ImageOps
            except ImportError:
                from PIL import ImageOps

            ImageOps.fit(image, (width, height), Image.ANTIALIAS
                        ).save(image_path, FMT, quality=QUAL)

    return resized_path(imagefield.url, size, method)



def crop(imagefield, size):
    """
    Template filter used to crop an image
    to make it fill the defined area.

    {% load image_tags %}
    {{ profile.picture|crop:"48x48" }}

    """
    return scale(imagefield, size, 'crop')


register.filter('scale', scale)
register.filter('crop', crop)

Those filters work with imagefields, but you can pass them any object that has path and url properties, even a dict.

Edited images will be saved under the same path as the original file.

GeoDjango with Django 1.2

I’ve been struggling with trying to use geodjango with django 1.2 when all you need to do is update your database settings.

If you are encountering errors like:

AttributeError: 'DatabaseOperations' object has no attribute 'geo_db_type'
AttributeError: 'module' object has no attribute 'GeoSQLCompiler'

Set your DATABASE_ENGINE correctly:

'ENGINE': 'django.contrib.gis.db.backends.postgis',

Error while doing syncdb with a GIS app and django 1.2

Looking for a domain name suggestion?

Computer suggestions

I use Visual thesorus to find interesting keywords and mix them with bustaname.com to find available suggestions.

Human suggestions

I am trying Picky domains and will update this post with results.

I have made the $50 payment and am waiting for further instructions.

SQL update for your django models, old school style

Let’s say you have added a new field to the profile model.

Use sqlall <app> to generate the SQL.

./manage.py sqlall profile
BEGIN;
CREATE TABLE `profile` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `user_id` integer NOT NULL UNIQUE,
    `new_field` integer UNSIGNED NOT NULL
)
;
ALTER TABLE `profile` ADD CONSTRAINT `user_id_refs_id_59efa1d8` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
COMMIT;

Using the output on line number 5, we fall back to SQL and edit the ALTER TABLE command to update the table model.

./manage.py dbshell
ALTER TABLE `profile` ADD
`new_field` integer UNSIGNED NOT NULL;

With django_extensions

If you have installed django_extensions, it is event easier.

./manage.py sqldiff profile
BEGIN;
-- Application: profile
-- Model: UserProfile
ALTER TABLE `profile`
	ADD `new_field` integer UNSIGNED;
COMMIT;
Always import your django settings this way!
from django.conf import settings

If you don’t, the settings won’t contain the default values.

Django settings documentation

Here’s the algorithm Django uses in compiling settings:

  • Load settings from global_settings.py.
  • Load settings from the specified settings file, overriding the global settings as necessary.

Links

http://docs.djangoproject.com/en/dev/topics/settings/#default-settings

http://code.djangoproject.com/browser/django/trunk/django/conf/__init__.py

Install django-extensions!!!
pip install -e git+git://github.com/django-extensions/django-extensions.git#egg=django_extensions

Automatically import your applications models in your python shell

./manage.py shell_plus
From 'auth' autoload: Permission, Group, User, Message
From 'my_app' autoload: MyModel
>>>

Visualize your project

./manage.py graph_models -a -g -o my_project_visualized.png

ForeignKeyAutocompleteAdmin

Instead of having a select box with every FK in the admin, you have an autocomplete text input.

# profile/admin.py
from django.contrib import admin
from django_extensions.admin import ForeignKeyAutocompleteAdmin

from .models import UserProfile

class UserProfileAdmin(ForeignKeyAutocompleteAdmin):
    related_search_fields = { 
        'user': ('username',),
    }   

admin.site.register(UserProfile, UserProfileAdmin)

Don’t forget to copy the django_extensions media in your static path (for javascript and css helpers).

And more…

Dive into the code, this is just the tip of the iceberg…

Links

http://github.com/django-extensions/django-extensions

Set MySQL storage engine to InnoDB for Django transactions

The advantage of InnoDB tables is transactions.

If you make multiple edits to the database and something goes wrong, you won’t be left with half the data updated. Either everything gets written or nothing.

Open your settings file:

DATABASES = { 
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '',                      
        'USER': '',     
        'PASSWORD': '',
        'OPTIONS': {
               "init_command": "SET storage_engine=INNODB",
        }   
    }   
}

Convert MyISAM tables to InnoDB

If your have already created your tables and they are using MyISAM, convert them.

python manage.py dbshell
SHOW TABLES;
SHOW CREATE TABLE <tablename>;
ALTER TABLE <tablename> ENGINE=INNODB;

Using transactions with django

# my_app/views.py
from django.db import transaction

@transaction.commit_on_success
def my_view(request):
    ....

Links

http://docs.djangoproject.com/en/dev/ref/databases/#creating-your-tables

http://docs.djangoproject.com/en/dev/topics/db/transactions/

http://en.wikipedia.org/wiki/Database_transaction