Maximilian Kindshofer
Maximilian Kindshofer

Reputation: 2843

Why can't I use model methods as defaults? What is self in Python/Django models anyway?

I have a very basic problem in understanding the behaviour of self.

Specifically, I don't understand why neither of the following two snippets of code work, the first one causing the interpreter to complain that

self is missing as an argument,

the second that

self couldn't be referenced.

class Model(models.Model)
    name = models.CharField(max_length=200)
    auto_gen_field = models.CharField(max_length=200, default=gen_field())

    def gen_field(self):
        return self.name + 'something'

class Model(models.Model)
    name = models.CharField(max_length=200)
    auto_gen_field = models.CharField(max_length=200, default=gen_field(self))

    def gen_field(self):
        return self.name + 'something'

The following code, which uses self as well, works fine:

class Model(models.Model)
    name = models.CHarField(max_length=200)
    auto_gen_field = models.CharField(max_length=200, blank=True)

    def gen_field(self):
        return self.name + 'something'

    def save(self):
        self.auto_gen_field = self.gen_field()
        super(Model, self).save(*args, **kwargs)

Why does the self reference exists in one place but not in the other?

How do I fix the first two snippets?

Upvotes: 4

Views: 1275

Answers (2)

Tobia Tesan
Tobia Tesan

Reputation: 1936

self is by convention (it is really a name like any other, what matters is that it is the first argument of your class methods) a reference to the current instance of the class, more or less like this in C++/Java.

Now, in

class Model(models.Model)
    name = models.CharField(max_length=200)
    auto_gen_field = models.CharField(max_length=200, default=gen_field())

When models.CharField(max_length=200, default=gen_field()) is evaluated there is no instance of the class, so you can't add a self in there. It doesn't and can't possibly make sense.

Now, from: https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.Field.default

Field.default¶

The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.

The default cannot be a mutable object (model instance, list, set, etc.), as a reference to the same instance of that object would be used as the default value in all new model instances. Instead, wrap the desired default in a callable.

You want to pass a callable - which basically is akin to a function pointer.

What would work is:

def some_function():
     return something

...

auto_gen_field = models.CharField(max_length=200, default=some_function)

You can not, however, use a model method like gen_field() as default because Django does not pass a model instance as the first argument of the provided callable.

You have to override save.

Upvotes: 3

Daniel Roseman
Daniel Roseman

Reputation: 600041

You're invoking the method at the time when the field is declared. At that point there is no self.

You don't want to do that anyway. Quite apart from any problems with self, doing so would return a static value for the default, so all instances would use the same value. That is why it is important to pass the callable: gen_field - not the result - gen_field().

(Note however that still won't work: the instance wouldn't be passed to the function because it's not being invoked as a method on an instance. Overriding save is the way to go.)

Upvotes: 8

Related Questions