user972014
user972014

Reputation: 3856

Adding code and methods to Django Model breaks it

I have a Django model similar to this:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.TextField(unique=False)

I wanted to add code to a person, outside of the models.py file. So I tried to import it, inherit from it and add code:e.g:

class MyPerson(db.Person):
    def __init__(self, person_name):
        super(MyPerson, self).__init__(name=person_name)

    def print_person_info(self):
        print(self.name)

I get an error similar to this one:

RuntimeError: Model class db.persons.models.Person doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

although of course in my settings.py file I do add my persons app to the INSTALLED_APPS

I guess I can add the code directly inside models.py but it seems to be very inconvenient as I want to add more code there, and for several models.

Any ideas?

Is it in general OK/Recommended to inherit from the model?

Upvotes: 2

Views: 334

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476659

I wanted to add code to a person, outside of the models.py file. So I tried to import it, inherit from it and add code.

If you inherit a Django model. Django sees this as an extra model (that inherits). So it has to construct an extra table at the database side that has a reference to the "parent object", and in some cases extra fields of you specify these. But since you probably defined this class outside models.py, it can not find an app_label, and as a result, a lot of tasks can not be carried out.

Is it in general OK/Recommended to inherit from the model?

No, only from abstract models, but this is another usecase than the one here. Inheriting from non-abstract models is possible, but still I would not recommend this, since it creates a lot of extra trouble at the database level.

Typically if you want to add behavior, you do this directly at the model class, like:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.TextField(unique=False)

    def print_person_info(self):
        print(self.name)

or in case the behavior is different from what you already defined, you can use a proxy model [Django-doc] to let two model classes work on the same database table, each with their behaviour, like:

class MyPerson(db.Person):

    class Meta:
        proxy = True

    def print_person_info(self):
        print(self.name)

But a proxy also can introduce a lot of extra "complexity", and therefore it is advisable to only use it as a "last resort".

It is really advisable not to patch the __init__ function, and definitely not by changing the parameters (if you do so, use *args, and **kwargs to pass all parameters), like:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.TextField(unique=False)

    def __init__(self, *args, **kwargs):
        # ... (do something) ...
        super(Person, self).__init__(*args, **kwargs)
        # ... (do something) ...

    def print_person_info(self):
        print(self.name)

In Django, a lot of classes assume that these can make model instances by calling the constructor without parameters, or by passing the field names with values. By altering this behaviour, it would require rewriting a lot of classes such that you can use your model(s) in forms, class-based views, etc.

Upvotes: 5

Related Questions