9

I'm simultaneously learning Python while picking up Django. I'm familiar with many other languages.

In the following code snippet, x is a class variable of class Foo.

class Foo(object):
    x = 9000

Given the previous declaration, the following works fine.

print Foo.x

The Django framework lets you create your model by defining Python classes. It makes fields out of the different class variables in your Python classes.

class Question(models.Model):
    question_text = models.CharField(max_length=200)

Why does the following code snippet:

#!/usr/bin/env
import os, django
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
django.setup()
from polls.models import Question, Choice
print Question.question_text

throw the following error:

AttributeError: type object 'Question' has no attribute 'question_text'

As far as I'm understanding everything my Question class has a single static member defined: Question.question_text.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
JDR
  • 237
  • 4
  • 8
  • what are you expecting it to print? Since the question_text field is a database attribute mapped class member it isn't intended to hold values, that's where the database kicks in. So if you're hoping for a string value to be printed it will not work. If this code were to work the best you could get is the string description of the class models.CharField, probably – Freestyle076 Dec 14 '14 at 23:44
  • 5
    Django models use a *metaclass* to alter what is normal class behaviour. Use `dir(Question)` and you'll see there are *different* attributes on that class now. This is custom behaviour just for Django models however. – Martijn Pieters Dec 15 '14 at 00:42
  • @Freestyle076 `` was exactly what I was hoping would show up on my screen. – JDR Dec 15 '14 at 03:21
  • @MartijnPieters Do you mind placing your reply as the answer to my question? I want to check-mark it as the selected answer to my quesetion. It's my first time using a language as "dynamic" as Python. I didn't realize the "looks like a duck" philosophy could reach the extents of re-defining classes on the fly. Thanks for the tip. – JDR Dec 15 '14 at 03:23

3 Answers3

7

Django models use a metaclass to alter what is normal class behaviour.

Use dir(Question) and you'll see there are different attributes on that class now. This is custom behaviour just for Django models however.

If you are curious you can study the metaclass __new__ method, but it does a lot of work specific to Object Relational Mapping tasks.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

Magic.

No, really.

Python classes aren't set-in-stone structure, like they are in C++. They are, themselves, just objects — instances of another type:

class Foo(object):
    pass

print(type(Foo))  # <class 'type'>

You can even make a class like you'd make any other object, by calling type. This:

class Bar(object):
    a = 1
    b = 2

Is really (more or less) syntactic sugar for this:

Bar = type('Bar', (object,), {'a': 1, 'b': 2})

type takes the name of your new class, a list of its superclasses, and a dict of all the attributes of the class, and spits out a new class.

But, because type is just a class like any other, it's possible to subclass it and give it different behavior. And this is what Django has done: it's created a subclass of type that does something different with the dict of attributes you pass to it.

You don't see this happening directly in your own code, but if you check type(models.Model), you'll find out its type is not type, but something specific to Django. It probably has "meta" in the name, because it's called a metaclass: the class of a class.

This is a fairly common pattern for making "declarative" libraries in Python, where the attributes of a class actually define some kind of structure. You can see the same thing in form validation (wtforms), schema validation (colander), other ORMs (sqlalchemy), and even the stdlib enum module.

Eevee
  • 47,412
  • 11
  • 95
  • 127
  • Note: I'm marking this as the "most helpful" answer, but Martijn Pieters response just under my original question was actually the more concise, to-the-point response to my question. – JDR Dec 15 '14 at 03:57
1

Question is an object of type type. You want an instance of Question:

>>> q= Question(text = "Does a dog have the buddha nature?")

Then you should get

q.text "Does a dog have the buddha nature?"

Note that this object will not persist unless you save() it:

>>> q.save()
Jon Kiparsky
  • 7,499
  • 2
  • 23
  • 38