""" Demonstration to use concepts in Django. A concept defines a generic model but doesn't implement the model itself. Concepts can be produced and consumed. A producer isn't aware of anything, we set details at runtime. A consumer isn't aware of the producer, but only knows the concept. """ from django.core.exceptions import ImproperlyConfigured from django.db import models from django.utils.importlib import import_module def load_class(fqc): """ Load a class by its fully qualified classname and return the class. """ try: mod_name, klass_name = fqc.rsplit('.', 1) mod = import_module(mod_name) except ImportError, e: raise ImproperlyConfigured(('Error importing module {0}: "{1}"'.format(mod_name, e))) try: klass = getattr(mod, klass_name) except AttributeError: raise ImproperlyConfigured(('Module "{0}" does not define a "{1}" class'.format(mod_name, klass_name))) return klass class Product(models.Model): """ A normal model that will be a producer. >>> product = Product.objects.create(name="regular_test") >>> product.name 'regular_test' """ name = models.CharField(max_length=255) class ProductConcept(object): """ The Product concept. A Product has a field description. """ mapping = {} @property def description(self): """ Proxy the function call to the producer """ return getattr(self, self.mapping['description']) @description.setter def description(self, value): """ Proxy the set operation to the producer """ setattr(self, self.mapping['description'], value) def __unicode__(self): return self.description # Map the concepts to producers CONCEPTS = [ ('{0}.{1}'.format(Product.__module__, Product.__name__), { 'description': 'name', }), ] class Meta: proxy = True # Set the producer at runtime for producer, mapping in CONCEPTS: attrs = { '__metaclass__': Meta, '__module__': __name__, 'mapping': mapping, } ProductConcept = type('ProductConcept', (load_class(producer), ProductConcept,), attrs) class Test(object): """ >>> obj = ProductConcept.objects.create(description="producer_test") >>> obj.description 'producer_test' >>> ProductConcept.objects.filter(**{ ... ProductConcept.mapping['description']: 'producer_test', ... }) [] """ class Order(models.Model): """ A normal model that consumes a concept in a foreign key. >>> product = ProductConcept.objects.create(description="consumer_test") >>> order = Order.objects.create(product=product) >>> order.product.description 'consumer_test' """ product = models.ForeignKey(ProductConcept)