tunarob
tunarob

Reputation: 3038

Django - how to make required to implement field?

I'm buildibg some abstract model for about 10 models. I need to make, somehow, that 1 field is not declared in abstract model, but MUST be declared in inheriting models. How to do that? Is there any way to use NotImplementedError?

Upvotes: 6

Views: 1638

Answers (2)

K Z
K Z

Reputation: 30453

I am afraid there isn't an easy way to achieve that, if possible at all, without digging deep into Django.

The main reason is that Field name "hiding" is not permitted in Django. What this means is that if you want to declare an abstract attribute in the base abstract class that is a Field instance, you will not be able to rewrite it in the child classes contrary to the normal Python class inheritance paradigm. To quote from the doc:

In normal Python class inheritance, it is permissible for a child class to override any attribute from the parent class. In Django, this is not permitted for attributes that are Field instances (at least, not at the moment). If a base class has a field called author, you cannot create another model field called author in any class that inherits from that base class.

Overriding fields in a parent model leads to difficulties in areas such as initializing new instances (specifying which field is being initialized in Model.init) and serialization. These are features which normal Python class inheritance doesn't have to deal with in quite the same way, so the difference between Django model inheritance and Python class inheritance isn't arbitrary.

This restriction only applies to attributes which are Field instances. Normal Python attributes can be overridden if you wish. It also only applies to the name of the attribute as Python sees it: if you are manually specifying the database column name, you can have the same column name appearing in both a child and an ancestor model for multi-table inheritance (they are columns in two different database tables).

Django will raise a FieldError if you override any model field in any ancestor model.

However, if the attribute is not a Field instance (very unlikely though), you will be able to achieve exactly what you want by using using @property decorator. Something like this should work:

class Person(models.Model):
    def __init__(self, *args, **kwargs):
        super(Person, self).__init__(*args, **kwargs)
        self.last_name

    first_name = models.CharField(max_length=30)

    @property
    def last_name(self):
        raise NotImplementedError

    class Meta:
        abstract = True

class Student(Person):
    home_group = models.CharField(max_length=5)
    last_name = "Doe"  # "models.CharField()" will not work!

class BadStudent(Person):
    home_group = models.CharField(max_length=5)
    # "NotImplmentedError" will be raised when instantiating BadStudent()

You may also want to take a look at abc.abstractproperty. I am not sure how it would work with Django's model inheritance though.

Upvotes: 7

rewritten
rewritten

Reputation: 16435

Why would you want to do it?? Which are the reasons the common field cannot be declared in the AbstractModel??

If you really want to do it, use the instructions here: add methods in subclasses within the super class constructor

Upvotes: -2

Related Questions