2

I have a class defined inside another class like this. Basically I am trying to override the save method in db.Model--it actually just the django.db.models.Model. But when I run this block of code, I see the an NameError.

class GameCenterDB:
    class GameCenterDBConfig:
        class Config:
            db_for_read = "game_center_db.slave"
            db_for_write = "default"

    class PublisherTab(GameCenterDBConfig, db.Model):
        publisher_id = db.PositiveIntegerField(primary_key=True)
        name = db.CharField(max_length=100)
        create_time = db.PositiveIntegerField()
        update_time = db.PositiveIntegerField()

        class Meta:
            db_table = u'publisher_tab'

        def save(self, *args, **kwargs):
            curr_time = int(time.time())
            if not self.create_time:
                self.create_time = curr_time
            self.update_time = curr_time
            # See the line below, this triggers an error
            # NameError: global name 'PublisherTab' is not defined
            super(PublisherTab, self).save(*args, **kwargs)

According to my understanding, when it is inside GameCenterDB, I should be able to use PublisherTab directly right?

NameError: global name 'PublisherTab' is not defined

Change save method like this will solve the error. But I just do not understand why.

def save(self, *args, **kwargs):
     curr_time = int(time.time())
     if not self.create_time:
         self.create_time = curr_time
     self.update_time = curr_time
     super(GameCenterDB.PublisherTab, self).save(*args, **kwargs)

Also, seems that class PublisherTab(GameCenterDBConfig, db.Model): is interpreted without any error and the mixin worked. Why GameCenterDBConfig can be used without any problem?

Will
  • 24,082
  • 14
  • 97
  • 108
Junchao Gu
  • 1,815
  • 6
  • 27
  • 43

2 Answers2

3

"According to my understanding, when it is inside GameCenterDB, I should be able to use PublisherTab directly right?"

Wrong. Python requires full qualification of class members with either the class or variable (typically 'self') prefix. This is true of any member variable declared within a class. E.g.:

class Foo:
    class Bar:
        quux = 1
    def f(self):
        print "Foo.Bar.quux: %d" % Foo.Bar.quux
        print "self.Bar.quux: %d" % self.Bar.quux
foo = Foo()
foo.f()

Now consider this example:

# scope is top-level module
class Foo:
   # scope is Foo
    class Bar:
        # scope is Foo.Bar
        quux = 1
    # scope is Foo
    Bar.quux = 2 # [A]
    try:
        print "x: %d" % x
    except NameError:
        print "x gave an error because it is outside scope"
    def f(self):
        # scope is Foo when we are defining but not when we are running!
        try:
            print "Bar.quux: %d" % Bar.quux
        except NameError:
            print "Bar.quux gave us an error because it is outside scope"
        print "Foo.Bar.quux: %d" % Foo.Bar.quux
        print "self.Bar.quux: %d" % self.Bar.quux
        print "x is in scope: %d" % x
# scope is top-level module again
x = 456
foo = Foo()
foo.f()

I've added the code at [A]. The program now prints "2" not "1".

Why don't you need to qualify Bar.quux at [A] but you do inside f()?

Because when [A] is run, the script is inside the scope of class Foo.

But when foo.f() is run, the script is inside the scope of the module because that is where you are calling it from. That's why you need to explicitly declare self in the method definition, and foo.f() is syntactic sugar for Foo.f(foo).

This is one of the less pleasing parts of Python. It makes sense is hard to understand.

James Fryer
  • 229
  • 1
  • 3
  • So is `PublisherTab` inheriting from `GameCenterDBConfig`? Why that part can be interpreted without any errors? – Junchao Gu Feb 02 '16 at 11:05
  • Because there you are defining PublisherTab and Python defines scope by nesting. I'll edit my answer to further explain. – James Fryer Feb 02 '16 at 11:17
0

You'll want to do it like this:

class GameCenterDB(object):
    class GameCenterDBConfig(object):
        class Config(object):
            pass

    class PublisherTab(GameCenterDB.GameCenterDBConfig, db.Model):
        pass

        class Meta:
            pass

        def save(self, *args, **kwargs):
            super(GameCenterDB.PublisherTab, self).save(*args, **kwargs)

You need to reference these class names from the global scope.

Will
  • 24,082
  • 14
  • 97
  • 108
  • So is `PublisherTab` inheriting from `GameCenterDBConfig`? Why that part can be interpreted without any errors? – Junchao Gu Feb 02 '16 at 11:05
  • Well it's not *inheriting*, technically, it's just *nested*. For the actual class definition, you're right, it's not needed, but it won't hurt. – Will Feb 02 '16 at 11:20