Inheritance at runtime (per model instance)

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Inheritance at runtime (per model instance)

guettli

Hi,

I am writing a workflow engine. The base workflow is stored in the DB.
But some code needs to written for most workflows.

This means I need code for a model instance (an not code per mode class).

To make this most pythonic, how could this be done?

My idea: since I want the workflow to be portable to several installations, I don't use
serial IDs for primary keys, but slug-fields. The slug-field could be a importable
classname like 'foo.workflow.MyClass'.

Now I want that the Workflow model instance inherits from MyClass. This must
be done at runtime. I think this could be done with a custom manager.

Does someone have a better approach, or feedback?

  Thomas


--
Thomas Guettler, http://www.thomas-guettler.de/
E-Mail: guettli (*) thomas-guettler + de

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to [hidden email]
To unsubscribe from this group, send email to [hidden email]
For more options, visit this group at http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|

Re: Inheritance at runtime (per model instance)

Malcolm Tredinnick

On Wed, 2009-08-12 at 11:39 +0200, Thomas Guettler wrote:

> Hi,
>
> I am writing a workflow engine. The base workflow is stored in the DB.
> But some code needs to written for most workflows.
>
> This means I need code for a model instance (an not code per mode class).
>
> To make this most pythonic, how could this be done?
>
> My idea: since I want the workflow to be portable to several installations, I don't use
> serial IDs for primary keys, but slug-fields. The slug-field could be a importable
> classname like 'foo.workflow.MyClass'.

You already have the content type table for referring to other model
objects. Why not use that so that you can maintain some referential
integrity.

I suspect, simply because you've been using Django forever and know
what's going on, that there is something subtle here I don't understand,
so feel free to tell me why I'm an idiot and this won't work.

>
> Now I want that the Workflow model instance inherits from MyClass. This must
> be done at runtime. I think this could be done with a custom manager.

This is where I become Old Conservative Malcolm and say that you might
want to consider NOT abusing model inheritance! :) I have reasons,
though, so let me explain...

I try to always relate this back to Python and what you can do in Python
with normal classes (Django's ORM just provides a way to store parts of
classes on disk). You can do what you want in Python, using the type()
command where you can create whatever base classes you like. Sadly, it's
harder than that when we introduce the persistence part -- storing in
the DB. Since inheritance is implemented via relations that are
constraints at the DB level, allowing arbitrary tables is not going to
be possible. Although I've tried to implement model inheritance so that
it works as transparently as possible, there are limits and this is one
of them -- it's a genuine leaky abstraction, because the database
requirement influences the functionality regardless of what
implementation I use.

A Django relation that points to any type of model already exists,
however: GenericForeignKey. Since you can ignore model inheritance in
Django and use a OneToOneField to link the models, you can do the same
thing with GenericForeignKey in the place of OneToOneField. So you end
up storing the "type" of the remote model and the pk of the instance you
are pointing to. No referential integrity at the database level there
(although we do enforce that the type is an existing content type).

Based on your brief description, I would use GenericForeignKeys here, I
think. But please elaborate if there's a reason that won't work for you.

Regards,
Malcolm


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to [hidden email]
To unsubscribe from this group, send email to [hidden email]
For more options, visit this group at http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|

Re: Inheritance at runtime (per model instance)

guettli

Hi Malcolm and others,

Malcolm Tredinnick schrieb:

> On Wed, 2009-08-12 at 11:39 +0200, Thomas Guettler wrote:
>> Hi,
>>
>> I am writing a workflow engine. The base workflow is stored in the DB.
>> But some code needs to written for most workflows.
>>
>> This means I need code for a model instance (and not code per mode class).
>>
>> To make this most pythonic, how could this be done?
>>
>> My idea: since I want the workflow to be portable to several installations, I don't use
>> serial IDs for primary keys, but slug-fields. The slug-field could be a importable
>> classname like 'foo.workflow.MyClass'.
>
> You already have the content type table for referring to other model
> objects. Why not use that so that you can maintain some referential
> integrity?

Content Types use serial keys. Take something like a ticket system:
Serial keys are good for the tickets. But if you have something like a
TicketTypeModel it is not a good solution. The customer uses the live
system and can create new TicketTypes. But during the same time,
I might create new TicketTypes in my development system. The serial IDs clash.

And the TicketTypes or (Workflow-Models) need to be installable for several customers.

>> Now I want that the Workflow model instance inherits from MyClass. This must
>> be done at runtime. I think this could be done with a custom manager.
>
> This is where I become Old Conservative Malcolm and say that you might
> want to consider NOT abusing model inheritance! :) I have reasons,
> though, so let me explain...

... thank you for this hint. Up to now, I don't need model inheritance
at runtime. The model instance would inherit from a normal python class.
I even did not think about it. But you did and that's good.
I drop my first idea, because I understand, that doing model inheritance at
runtime does smell bad.

>
> I try to always relate this back to Python and what you can do in Python
> with normal classes (Django's ORM just provides a way to store parts of
> classes on disk). You can do what you want in Python, using the type()
> command where you can create whatever base classes you like.
....
Yes, I have your opinion, too.

  Thomas G├╝ttler

--
Thomas Guettler, http://www.thomas-guettler.de/
E-Mail: guettli (*) thomas-guettler + de

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to [hidden email]
To unsubscribe from this group, send email to [hidden email]
For more options, visit this group at http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|

Re: Inheritance at runtime (per model instance)

Malcolm Tredinnick

On Wed, 2009-08-12 at 14:24 +0200, Thomas Guettler wrote:
> Hi Malcolm and others,
>
> Malcolm Tredinnick schrieb:
[...]

 
> > You already have the content type table for referring to other model
> > objects. Why not use that so that you can maintain some referential
> > integrity?
>
> Content Types use serial keys. Take something like a ticket system:
> Serial keys are good for the tickets. But if you have something like a
> TicketTypeModel it is not a good solution. The customer uses the live
> system and can create new TicketTypes. But during the same time,
> I might create new TicketTypes in my development system. The serial IDs clash.

Ah, ok. So you'll end up with something like your own version of the
content type system. Makes sense. Providing your model names are going
to be different from your customer's developed versions, your plan makes
sense.

> And the TicketTypes or (Workflow-Models) need to be installable for several customers.

This is the only reason I would be tempted not to just use the import
path. You really want to namespace the identifiers and model names might
clash if they're common enough. I would be very tempted to prefix some
identifier with a customer-specific identifier (as a development
recommendation, not enforced in the code). So you identifiers would be
"tg_<something>" and your customer 1 might use "aa_<something>", etc.
The prefix used to generate the ids could even be a setting if you could
auto-create the ids from the model name/import path.

But I might be over-engineering this. You understand your target
audience -- particularly the number and habits of the target audience --
better than me. So this is just me thinking out loud.

Regards,
Malcolm



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to [hidden email]
To unsubscribe from this group, send email to [hidden email]
For more options, visit this group at http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply | Threaded
Open this post in threaded view
|

Re: Inheritance at runtime (per model instance)

guettli

Hi Malcolm,

Malcolm Tredinnick schrieb:

> On Wed, 2009-08-12 at 14:24 +0200, Thomas Guettler wrote:
>> Hi Malcolm and others,
>>
>> Malcolm Tredinnick schrieb:
> [...]
>
>  
>>> You already have the content type table for referring to other model
>>> objects. Why not use that so that you can maintain some referential
>>> integrity?
>> Content Types use serial keys. Take something like a ticket system:
>> Serial keys are good for the tickets. But if you have something like a
>> TicketTypeModel it is not a good solution. The customer uses the live
>> system and can create new TicketTypes. But during the same time,
>> I might create new TicketTypes in my development system. The serial IDs clash.
>
> Ah, ok. So you'll end up with something like your own version of the
> content type system. Makes sense. Providing your model names are going
> to be different from your customer's developed versions, your plan makes
> sense.

It's a compliment for me, if you say "makes sense". Thank you.

I uploaded my implementation called ImportObject here:
http://www.djangosnippets.org/snippets/1681/

> This is the only reason I would be tempted not to just use the import
> path. You really want to namespace the identifiers and model names might
> clash if they're common enough.

The import path should be absolute. Example: myapp.models.MyModel or myapp.foo.MyPlainPythonClass.
Theses paths should be unique. The drawback is, that is harder to refactor code. You
need to modify the DB tables if you change the layout of the code.

If a customer needs a different class, you could add a hook somewhere to load it.

 Regards,
   Thomas

--
Thomas Guettler, http://www.thomas-guettler.de/
E-Mail: guettli (*) thomas-guettler + de

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to [hidden email]
To unsubscribe from this group, send email to [hidden email]
For more options, visit this group at http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---