views:

267

answers:

2

I would like to use properties from an inheriting model's Meta class to configure a field defined in an abstract model higher up the inheritance tree:

class NamedModel(models.Model):
    class Meta:
        abstract = True
        verbose_name = 'object'

    name = models.CharField("Name",
        max_length=200,
        db_index=True,
        help_text="A meaningful name for this %s." % Meta.verbose_name)
        # see what I'm trying to do here?
    )
    ...

class OwnedModel(NamedModel):
    class Meta(NamedModel.Meta):
        verbose_name = 'owned object'

I would like the help text on the name field of OwnedModel forms to say 'A meaningful name for this owned object'. But it does not: the word 'owned' is missing, which would suggest that the verbose_name from the NamedModel.Meta is used when the model is set up, not OwnedModel.Meta.

This isn't quite what I expect from an inheritance point of view: is there some way to get the field to be created whereby Meta.verbose_name refers to the value on the non-abstract model class, not the abstract one on which the field was defined?

Or am I being daft?

(This may seem like a trivial example, and it is: but it's just to illustrate the point of something more important and complex I am trying to do)

Many thanks in advance.

+1  A: 

I think this happens because Meta.verbose_name is used and NamedModel.name is created when class NamedModel is parsed. So later, when class OwnedModel gets parsed, there is no chance to change anything.

Maybe you can set the help_text property on OwnedModel.name later on, but this may change NamedModel.name also.

In similar situations I have put the variable parts in class attribute of the model (not Meta) and then used the by run time methods/properties to generate the texts I need.

Ber
Yes, I think that's it. Having followed the flow I'm clearer on the lifecycle: the fields are evaluated when the class is parsed, so the inheritance isn't considered.And I've tried class attributes outside of meta... same problem of course.I'm happy to override fields classes and I may use that (+ what you suggest) to get the desired effect.
A: 

In fact I ended up doing the following. The base model gets given a dynamic_field_definition() class method, which can be used to patch up the fields, with the cls argument being the correct (inheriting) class. That means that that cls' Meta attributes are of that correct child, not the original base.

I then wire up that method to get called on the class_prepared signal, so that you know everything's otherwise ready.

class NamedModel(models.Model):
    ...
    @classmethod
    def dynamic_field_definition(cls):
        pass

def dynamic_field_definition(sender, **kwargs):
    if issubclass(sender, NamedModel):
        sender.dynamic_field_definition()
class_prepared.connect(dynamic_field_definition)

Then the field properties that vary with model class are simply reconfigured by that class method (or more likely the method as overridden in derived classes).

It's a slightly hacky way to bring a last little bit of OO-ness to Django models, but it works fine for my purpose.