eSurfsnake
eSurfsnake

Reputation: 689

using meta classes properly in Python 3 / Django

I keep looking at references and examples, but still can't quite get this right.

Django provides lots of examples of using metaclasses as part of a factory system that creates all sorts of methods, etc., specific to the values passed in. The idea is clear. But how it really works is a bit mysterious.

A typical piece of Django (straight from the Django 'getting started' tutorial)code looks like this:

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Now, later on, there will be actual instances of these classes, e.g.:

q1 = Question(question_text='my question', pub_date='01-01-2017')

Yet, for all the world, within the class definition, question_text and pub_data look like they are class-wide objects (i.e., shared by all instances of that class, such as q1, q2, etc.).

The more traditional way to do this and maintain isolation would be for there to be a 'self' in front of each, so that the class def looked like:

class Question(models.Model):
    self.question_text = models.CharField(max_length=200)
    self.pub_date = models.DateTimeField('date published')

I even tried looking at the source code for Django. Indeed, models.Model is a class which has a meta-class above it that gets invoked whenever a new class gets declared that is based on models.Model. But the code is fairly cryptic.

Is there something idiomatic going on here? I presume that the variables declared in the class are not meant to be shared across instances. Perhaps, I suppose, declaring the vars (and their type, if you will) right up there is simply a way of shoving them into an internal attribute dict, and the metaclass does some magic to make things happen that lead to the sort of outcome that seems more logical.

But, somehow, as code goes along and one creates another instance like:

q2 = Question(question_text='my next question', pub_date='01-02-2017')

The demo code works just fine if you reference q1.question_text or q2.pub_date, so that they acr as if the variables declared in the class declarations did, indeed, have a 'self.' pre-pended.

If curious, here is the Django source (Model is at line 383 and the meta-classs above it): https://github.com/django/django/blob/master/django/db/models/base.py

Upvotes: 0

Views: 259

Answers (1)

cwallenpoole
cwallenpoole

Reputation: 82028

The fields, meaning the representation of columns in the DB schema, are class-level, but the values are instance level.

Say you have a IntegerField:

class WithInt(models.Model):
    fld = models.IntegerField()

This means:

type(WithInt.fld) # IntegerField
inst = WithInt.objects.get(1)
type(inst.fld) # int

Upvotes: 2

Related Questions