0

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.

schmilblick
  • 1,917
  • 1
  • 18
  • 25

2 Answers2

1

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!

Community
  • 1
  • 1
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
0

If you know that a customer's never going to order a Product, why not make it an abstract model and then just do the sorting yourself? Accessing a user's books, cars, or house cleanings becomes easy at that point, because you can just use c.book_set.all(), c.car_set.all(), and so on.

You'd have to do the sorting yourself if you wanted, say, a list of all Products attached to a contract - but it's not hard to write a sorted wrapper to put everything into a list for you if that's what you're looking for.

girasquid
  • 15,121
  • 2
  • 48
  • 58
  • How Contract will contain Book,Car and HouseCleaning objects which are different from each other? – Ankit Jaiswal Dec 16 '10 at 18:08
  • Easy: `def product_set(self): products = self.book_set.all() + self.car_set.all() + self.housecleaning_set.all() return sorted(products, lambda i: i.created_date)` – girasquid Dec 16 '10 at 18:11
  • So every time I add a new product, I have to add another c.newproduct_set.all() ? Im not comfortable with that solution... Am i too picky? :) – schmilblick Dec 17 '10 at 10:05
  • Well, ultimately it's up to you - that's how I'd do it, but you're free to do what you'd like. You might want to look into how Django models track their related items - it's possible you could do some introspection to handle the c.newproduct_set.all() part for you. – girasquid Dec 17 '10 at 14:37