Search code examples
pythondjangodjango-modelsapplication-design

Is it possible to connect different Product-objects to one Contract?


My problem:
I want my Customers to be able to connect many different Products to one Contract, and the code to list all Products attached to a Contract feels dirty.

My Models

class Contract(models.Model):
    # The contract, a customer can have many contracts
    customer = models.ForeignKey(Customer)


class Product(models.Model):
    # A contract can have many different products
    contract = models.ForeignKey(Contract)
    start_fee = models.PositiveIntegerField()

# A customer may have ordered a Car, a Book and a HouseCleaning, 
# 3 completly different Products

class Car(Product):
    brand = models.CharField(max_length=32)

class Book(Product):
    author = models.ForeignKey(Author)

class HouseCleaning(Product):
    address = models.TextField()

To list all the products connected to a Contract, the code looks something like this:

c = Contract.objects.get(pk=1)
for product in c.product_set.all():
    print product.book # Will raise an Exception if this product isnt a Book

I can't find any sane way to find out what kind of product a Product is!

My current solution
I've solved it like this... but this whole mess feels... wrong. I'd be happy for any pointers in the right direction.

class Product(models.Model):
    # (as above + this method)

    def getit(self):
        current_module = sys.modules[__name__]
        classes = [ obj for name, obj in inspect.getmembers(current_module)
                    if inspect.isclass(obj) and Product in obj.__bases__ ]

        for prodclass in classes:
            classname = prodclass.__name__.lower()
            if hasattr(self, classname):
                return getattr(self, classname)

        raise Exception("No subproduct attached to Product") 

So i can fetch each specific product like this (pseudo-ish code) :

c = Contract.objects.get(pk=1)
for product in c.product_set.all():
    print product.getit()

to list all the "real" products, and not just the baseproduct instance.

What I need help with
Sugggestions to do this in some kind of sane way.
I don't mind having to abstract everything away a bunch of steps, just to get cleaner code.


Solution

  • This other stack question looks directly related -- maybe it will help?

    >>> Product.objects.all()
    [<SimpleProduct: ...>, <OtherProduct: ...>, <BlueProduct: ...>, ...]
    

    Subclassed django models with integrated querysets

    Specifically, the snippet has subclassed models Meal Salad(Meal)

    http://djangosnippets.org/snippets/1034/

    Good luck!