Skip to content

Instantly share code, notes, and snippets.

@dzerrenner
Created April 28, 2014 19:19
Show Gist options
  • Save dzerrenner/11381487 to your computer and use it in GitHub Desktop.
Save dzerrenner/11381487 to your computer and use it in GitHub Desktop.
Metaclass that adds couter methods for reverse foreign keys of Django models.
class ForeignCountMeta(ModelBase):
"""
Metaclass that adds couter methods for reverse foreign keys of Django models.
The methods that will be creates are defined in the models Meta options:
class CountedParent(models.Model, metaclass=ForeignCounterMeta):
class Meta:
counted_fields = {
"child1_count": {'relation_name': 'child1', 'short_description': 'Child 1 count'},
"child2_count": {'relation_name': 'child2', 'short_description': 'Child 1 count'},
}
class Child1(models.Model):
parent = ForeignKey(to=CountedParent)
class Child2(models.Model):
parent = ForeignKey(to=CountedParent)
With a CountedParent instance:
print(counted_parent.child1_count())
print(counted_parent.child2_count())
"""
def __init__(cls, name, bases, nmspc):
# check if the Meta options are available, not sure if this is necessary
if cls._meta and cls._meta.counted_fields:
def make_count(rel_name):
""" create a counter method for a specific relation """
def _count(self):
# results in "return self.rel_name_set.count()"
return getattr(self, "%s_set" % rel_name).count()
return _count
# do it for all items in the counted_fields option
for method_name, args in cls._meta.counted_fields.items():
# remove "relation_name" from the attributes, see further down
relation_name = args.pop('relation_name')
# create the counter method
count_method = make_count(relation_name)
# set the remaining attributes as attributes on the new method:
for attr, value in args.items():
setattr(count_method, attr, value)
setattr(cls, method_name, count_method)
super(ForeignCountMeta, cls).__init__(name, bases, nmspc)
@dzerrenner
Copy link
Author

You have to add the counted_fields attribute to the Meta-Options in your settings.py:

import django.db.models.options as options
options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('counted_fields', )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment