Subclass Django ModelBase (metaclass for Django models)
Question: I want some of my Django models to have an "owner" property. I may need to
change or augment the logic later, and the logic is reused across many
classes. So I'd like to just inherit from an `Owned` class that lets me store
the user who created the class. I'm not trying to populate the field yet, I
just need it to exist.

First I tried this:

    
    
    from django.db import models
    from django.contrib.auth.models import User
    
    class Owned(models.Model):
        owner = models.ForeignKey(User, related_name='owner')
    
        class Meta:
            abstract = True
    

But when I inherited from `Owned` in several subclasses, I got a Django
reverse accessor error: [Django Reverse Accessor
Clashes](http://stackoverflow.com/questions/26955319/django-reverse-accessor-
clashes)

It looks like this "owner" property needs to have a different "related_name"
in subclasses of the `Owned` class.

So I tried this:

    
    
    from django.db import models
    from django.db.models.base import ModelBase
    from django.contrib.auth.models import User
    
    
    class _OwnedMeta(ModelBase):
        '''
        Should makes "Owned" class below work.
        Gets around problem with reverse accessor clashes:
        '''
        def __init__(cls, name, bases, dct):
            related_name = '{}_owner'.format(name)
            dct['owner'] = models.ForeignKey(User, related_name=related_name)
    
            super(_OwnedMeta, cls).__init__(name, bases, dct)
    
    
    class Owned(models.Model):
        '''
        Instances get an "owner" attribute
        that is a foreign key to '<class_name>_owner'
        '''
        __metaclass__ = _OwnedMeta
        owner = models.ForeignKey(User, related_name='owner')
    
        class Meta:
            abstract = True
    

The idea is that when I subclass `Owned` I'll get an `owner` property with
related name `*class_name*_owner`.

Like this:

    
    
    Class Subclass(Owned):
        pass
    
    instance = Subclass()
    

and now, if this worked, instance.subclassed would be a foreign key to the
Django `User` model and the `related_name` would be "Subclass_owner."

But it doesn't work. This is an excerpt of the error message:

    
    
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/base.py", line 297, in add_to_class
        value.contribute_to_class(cls, name)
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 1588, in contribute_to_class
        super(ForeignObject, self).contribute_to_class(cls, name, virtual_only=virtual_only)
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 272, in contribute_to_class
        add_lazy_relation(cls, self, other, resolve_related_class)
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 84, in add_lazy_relation
        operation(field, model, cls)
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 271, in resolve_related_class
        field.do_related_class(model, cls)
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 307, in do_related_class
        self.set_attributes_from_rel()
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 304, in set_attributes_from_rel
        self.rel.set_field_name()
      File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 1259, in set_field_name
        self.field_name = self.field_name or self.to._meta.pk.name
    AttributeError: 'NoneType' object has no attribute 'name'
    

What am I doing wrong?


Answer: Actually, django solves _exactly_ your problem (of having a foreign key with a
related_name to an abstract class)! Please check the docs @
<https://docs.djangoproject.com/en/1.8/topics/db/models/#be-careful-with-
related-name>

Copying from there for answer completeness:

> If you are using the related_name attribute on a ForeignKey or
> ManyToManyField, you must always specify a unique reverse name for the
> field. This would normally cause a problem in abstract base classes, since
> the fields on this class are included into each of the child classes, with
> exactly the same values for the attributes (including related_name) each
> time.
>
> To work around this problem, when you are using related_name in an abstract
> base class (only), part of the name should contain `'%(app_label)s'` and
> `'%(class)s'`.
>
> `'%(class)s'` is replaced by the lower-cased name of the child class that
> the field is used in. `'%(app_label)s'` is replaced by the lower-cased name
> of the app the child class is contained within. Each installed application
> name must be unique and the model class names within each app must also be
> unique, therefore the resulting name will end up being different.

So, for example in your case, just change `Owned` to:

    
    
    
    class Owned(models.Model):
        owner = models.ForeignKey(User, related_name='%(app_label)s_%(class)s_owner')
    
        class Meta:
            abstract = True
    
    

