sedavidw
sedavidw

Reputation: 11691

Django 1.7 migration cannot serialize a class method

I have a very basic class that looks something like the following:

class Car(Model):

    name = CharField(max_length=255, unique=True)

    @classmethod
    def create_simple_examples(cls):
        for c in ['Sedan', 'Coupe', 'Van', 'SUV']:
            cls.objects.get_or_create(name=c)

    @classmethod
    def get_default(cls):
        c, _ = cls.objects.get_or_create(name='Sedan')
        return c

    def __unicode__(self):
        return self.name

I am trying to add it to a django app. I have the two class methods to 1. a function to populate the table quickly, and 2. to grab a default one which will be used often.

When I run

python manage.py makemigrations myapp

I get the following error

ValueError: Cannot serialize: <bound method ModelBase.get_default of <class 'crunch.django.myapp.models.Car'>>

I am not quite sure why it's trying to serialize my get_default function as that's not really part of the migration of the table itself. Any help would be greatly appreciated

UPDATE I think I may have found the source of the problem (still not sure how to fix it though...)

I have other classes that are FKing to my new class, and the default uses my default above...something like this

class OtherClass(Model):

    car = ForeignKey(Car, default=Car.get_default)

It looks like the migration is trying to serialize the function because of this. Any tips on how to get around this?

Upvotes: 4

Views: 4239

Answers (2)

karthikr
karthikr

Reputation: 99620

Add the @deconstructible decorator to the classes which have a classmethod

from django.utils.deconstruct import deconstructible

@deconstructible
class Car(Model):
    ...

More documentation on deconstructible can be found here

Upvotes: 3

Josh Kelley
Josh Kelley

Reputation: 58352

As explained in Django's migrations docs, Django can serialize function and method references, (in Python 3) unbound methods used from within the class body, and a bunch of other stuff, but it can't serialize everything.

In this case, because you've made get_default a @classmethod, Car.get_default is a bound method (i.e., it takes an implicit reference to Car as its first parameter), rather than a plain function or method reference, and Django doesn't know what to do with that.

Try making get_default a @staticmethod instead, or make a free function (top-level function) that calls Car.get_default.

Upvotes: 3

Related Questions