Ponytech

Technology for ponies

Jun 17, 2013

Check all items in a list are contained in an other list

In some code I wrote today I needed a way to check that all items in a list are contained in an other list. And I came up with a one-liner I found useful enough for sharing :

>>> big_list = [1,2,3]
>>> small_list = [1,3]
>>> all([i in big_list for i in small_list])
True
>>> small_list = [1,4]
>>> all([i in big_list for i in small_list])
False

Apr 29, 2013

Save a model without triggering a signal nor the save() method

It happens you may want to save one of your model but without triggering any pre_save / post_save signal, nor the save() method of your model. Reasons could be, your are restoring some fixtures data, you don't want to update the object modification date that is configured with auto_now = True, you want to skip some validations, etc.

There had been a ticket opened in the past to request such a feature for Django but this request had been rejected.

So here is a quick tip to allow you to achieve this behavior if you really have to : you can use django bulk update feature. Because this function only performs a raw SQL query its does not actually knows what object you are updating no signals and no save() methods are triggered / called.

Let say you have the following model with a DateTimeField, auto_now set to True:

class Document(models.Model)
   title = models.CharField(max_length=255)
   last_modified = models.DateTimeField(auto_now=True)

To update the title of a Document without updating his last_modified date you can do:

doc = Document.objects.get(title='Old title')
Document.objects.filter(id=doc.id).update(title='New title')

Mar 31, 2013

Migrate your user profile data to Django 1.5 custom user model

One of the major feature of Django 1.5 is the new custom user model. This article is a step by step guide on how to migrate your existing user data using the legacy user profile (which will be dropped in version 1.7) to a custom user model without losing data in the process.

This guide will use the excellent South framework to handle the migration. If you are not already using South, then you should! But don't worry, it's not too late to convert your project to South.

Initial set up

Let's suppose we have an app called myapp and the following model which allows us to store user's date of birth along with the user model:

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
       user = models.ForeignKey(User, unique=True)
       birthdate = models.DateField('Date of birth', null=True)

And we use the deprecated user profile setting : AUTH_PROFILE_MODULE='myapp.UserProfile' . The aim of this article is to get rid of this extra model / table by integrating the birth date directly into the user model.

Convert project to South

First step is to convert the project to South. If you are already using South, you can skip to the next step. Install South with pip install south and add south to your INSTALLED_APPS. Converting your app is as easy as

./manage.py syncdb

./manage.py convert_to_south myapp

Prepare the migration

First create an empty MyUser model that ineherit from AbstractUser

from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
       pass

and create an empty schema migration :

./manage.py schemamigration myapp --empty rename_auth_user_to_myapp_my_user

This creates an empty schema migration file in myapp/migrations/ named 0002_rename_auth_user_to_myapp_my_user.py. Let's edit that file and just add rename instructions in order to easily maintain all our existing users:

def forwards(self, orm):
    db.rename_table('auth_user', 'myapp_myuser')

def backwards(self, orm):
    db.rename_table('myapp_myuser', 'auth_user')

Now we want that extra birthdate column for our user, so turn your empty MyUser model to:

class MyUser(AbstractUser):
       birthdate = models.DateField('Date of birth', null=True)

and make an auto migration to get the field added to the table:

./manage.py schemamigration myapp --auto

It is now time to actually copy the date of birth to the new model using a data migration. Start a new data migration with:

./manage.py datamigration myapp copy_birthdate

Edit the migration file 0004_copy_birthdate with:

def forwards(self, orm):
    for userprofile in orm['myapp.UserProfile'].objects.all():
        user = orm['myapp.MyUser'].objects.get(id=userprofile.user_id)
        user.birthdate = userprofile.birthdate
        user.save()

If you ever want to go back in your migrations list you have to copy birthdates from MyUser back to UserProfile in the backwards() method. I let you write this migration as an exercice!

Finally we can get rid of the UserProfile model / table. Remove this model from models.py and create one more auto schema migration:

./manage.py schemamigration myapp --auto

Migrate

To sum up you should have the following migration files in your migration folder:

  • 0001_initial.py
  • 0002_migrate_to_custom_user.py
  • 0003_auto__add_field_myuser_birthdate.py
  • 0004_copy_birthdate.py
  • 0005_auto__del_userprofile.py

Let's actually apply those migrations with:

./manage.py migrate myapp

And voila! You data had been converted to new custom user model. To make use of it remove AUTH_PROFILE_MODULE from your settings and replace it by AUTH_USER_MODEL = 'myapp.MyUser'.

For more info on how to use the cutom user model consult the django documentation.

← Previous Next → Page 3 of 4