views:

205

answers:

2

Say, there is a Page that has many blocks associated with it. And each block needs custom rendering, saving and data.

Simplest it is, from the code point of view, to define different classes (hence, models) for each of these models. Simplified as follows:

class Page(models.Model):
    name = models.CharField(max_length=64)

class Block(models.Model):
    page = models.ForeignKey(Page)

    class Meta():
        abstract = True

class BlockType1(Block):

    other_data = models.CharField(max_length=32)

    def render(self):
        """Some "stuff" here """
        pass

class BlockType2(Block):

    other_data2 = models.CharField(max_length=32)

    def render(self):
        """Some "other stuff" here """
        pass

But then,

  • Even with this code, I can't do a query like page.block_set.all() to obtain all the different blocks, irrespective of the block type.
  • The reason for the above is that, each model defines a different table; Working around to accomplish it using a linking model and generic foreign keys, can solve the problem, but it still leaves multiple database tables queries per page.

What would be the right way to model it? Can the generic foreign keys (or something else) be used in some way, to store the data preferably in the same database table, yet achieve inheritance paradigms.

Update:

My point was, How can I still get the OOP paradigms to work. Using a same method with so many ifs is not what I wanted to do.

The best solution, seems to me, is to create separate standard python class (Preferably in a different blocks.py), that defines a save which saves the data and its "type" by instantiating the same model. Then create a template tag and a filter that calls the render, save, and other methods based on the model's type.

+3  A: 

Don't model the page in the database. Pages are a presentation thing.

First -- and foremost -- get the data right.

"And each block needs custom rendering, saving and data." Break this down: you have unique data. Ignore the "block" and "rendering" from a model perspective. Just define the data without regard to presentation.

Seriously. Just define the data in the model without any consideration of presentation or rending or anything else. Get the data model right.

If you confuse the model and the presentation, you'll never get anything to work well. And if you do get it to work, you'll never be able to extend or reuse it.

Second -- only after the data model is right -- you can turn to presentation.

Your "blocks" may be done simply with HTML <div> tags and a style sheet. Try that first. After all, the model works and is very simple. This is just HTML and CSS, separate from the model.

Your "blocks" may require custom template tags to create more complex, conditional HTML. Try that second.

Your "blocks" may -- in an extreme case -- be so complex that you have to write a specialized view function to transform several objects into HTML. This is very, very rare. You should not do this until you are sure that you can't do this with template tags.


Edit.

"query different external data sources"

"separate simple classes (not Models) that have a save method, that write to the same database table."

You have three completely different, unrelated, separate things.

  • Model. The persistent model. With the save() method. These do very, very little. They have attributes and a few methods. No "query different external data sources". No "rendering in HTML".

  • External Data Sources. These are ordinary Python classes that acquire data. These objects (1) get external data and (2) create Model objects. And nothing else. No "persistence". No "rendering in HTML".

  • Presentation. These are ordinary Django templates that present the Model objects. No external query. No persistence.

S.Lott
Point about Right data model taken, thanks. In my case, for different block types, I need to query different external data sources and render in a completely different html. Also, cache the data (at block level) So, block tags doesn't really help. I am thinking of just separate simple classes (not Models) that have a save method, that write to the same database table.
Lakshman Prasad
+1  A: 

I just finished a prototype of system that has this problem in spades: a base Product class and about 200 detail classes that vary wildly. There are many situations where we are doing general queries against Product, but then want to to deal with the subclass-specific details during rendering. E.g. get all Products from Vendor X, but display with slightly different templates for each group from a specific subclass.

I added hidden fields for a GenericForeignKey to the base class and it auto-fills the content_type & object_id of the child class at save() time. When we have a generic Product object we can say obj = prod.detail and then work directly with the subclass object. Took about 20 lines of code and it works great.

The one gotcha we ran into during testing was that manage.py dumpdata followed by manage.py loaddata kept throwing Integrity Errors. Turns out this is a well-known problem and a fix is expected in the 1.2 release. We work around it by using mysql commands to dump/reload the test dataset.

Peter Rowell