Table-less model as base class?

91 views
Skip to first unread message

Eric Walstad

unread,
Sep 18, 2005, 3:39:49 PM9/18/05
to Django users
Hi all,

I'd like to make a base class for my model classes that defines some
fields but doesn't result in a table in the database. If my base class
is derived from meta.Model, then django makes a table for it in the
database.

Is it possible to do what I want, move common fields to a super class,
without generating a table for that super class?

Thanks,

Eric.

contrived example follows (I don't want the 'experiment_mybaseclasss'
table created):

experiment/models/experiment.py:
from django.core import meta
class myBaseClass(meta.Model):
created_on = meta.DateTimeField(auto_now_add=True)
modified_on = meta.DateTimeField(auto_now=True)
class myDerivedClass(myBaseClass):
name = meta.CharField(maxlength=25)
class META:
module_name = 'my_derived_class'


$ django-admin.py sql experiment
BEGIN;
CREATE TABLE experiment_mybaseclasss (
id serial NOT NULL PRIMARY KEY,
created_on timestamp with time zone NOT NULL,
modified_on timestamp with time zone NOT NULL
);
CREATE TABLE experiment_my_derived_class (
id serial NOT NULL PRIMARY KEY,
modified_on timestamp with time zone NOT NULL,
created_on timestamp with time zone NOT NULL,
name varchar(25) NOT NULL
);
COMMIT;

Adrian Holovaty

unread,
Sep 18, 2005, 3:53:21 PM9/18/05
to django...@googlegroups.com
On 9/18/05, Eric Walstad <ewal...@gmail.com> wrote:
> I'd like to make a base class for my model classes that defines some
> fields but doesn't result in a table in the database. If my base class
> is derived from meta.Model, then django makes a table for it in the
> database.
>
> Is it possible to do what I want, move common fields to a super class,
> without generating a table for that super class?

It's not currently possible, but that exact functionality is on the
to-do list: http://code.djangoproject.com/ticket/419 .

Adrian

--
Adrian Holovaty
holovaty.com | djangoproject.com | chicagocrime.org

Robert Wittams

unread,
Sep 18, 2005, 5:54:44 PM9/18/05
to django...@googlegroups.com
Adrian Holovaty wrote:
> On 9/18/05, Eric Walstad <ewal...@gmail.com> wrote:
>
>>I'd like to make a base class for my model classes that defines some
>>fields but doesn't result in a table in the database. If my base class
>>is derived from meta.Model, then django makes a table for it in the
>>database.
>>
>>Is it possible to do what I want, move common fields to a super class,
>>without generating a table for that super class?
>
>
> It's not currently possible, but that exact functionality is on the
> to-do list: http://code.djangoproject.com/ticket/419 .
>
> Adrian
>

Is it possible or planned to make the generated methods for superclasses
aggregate sub class instances?

Eg

class animal(meta.Model):
name = TextField()

class dog(animal):
pass

class cat(animal):
pass


Then animal.get_list(name__exact='Fluff') would return instances of both
cats and dogs.

As far as I can tell this doesn't currently work.

Eric Walstad

unread,
Sep 18, 2005, 7:30:28 PM9/18/05
to Django users
Adrian Holovaty wrote:
> On 9/18/05, Eric Walstad <ewal...@gmail.com> wrote:
[...]

> > Is it possible to do what I want, move common fields to a super class,
> > without generating a table for that super class?
>
> It's not currently possible, but that exact functionality is on the
> to-do list: http://code.djangoproject.com/ticket/419 .
>
> Adrian

Hi Adrian,
I'm short on time, but I think I may have hacked up something that
works (for me) and passes the unit tests.

Eric.

[ewalstad@pasauran django_src]$ diff -u
django/core/meta/__init__.py.orig django/core/meta/__init__.py
--- django/core/meta/__init__.py.orig 2005-09-18 14:51:18.000000000
-0700
+++ django/core/meta/__init__.py 2005-09-18 15:59:20.000000000
-0700
@@ -412,6 +412,11 @@
# attribute order.
fields.sort(lambda x, y: x.creation_counter -
y.creation_counter)

+ # Should this class generate database tables (Default is Yes)?
+ # This has the ultimate effect of keeping this class out of
the _MODELS
+ # list.
+ create_table = not (meta_attrs.pop('no_table', False))
+
# If this model is a subclass of another model, create an
Options
# object by first copying the base class's _meta and then
updating it
# with the overrides from this class.
@@ -673,7 +678,9 @@
# contain this list:
# [<class 'django.models.polls.Poll'>, <class
'django.models.polls.Choice'>]
# Don't do this if replaces_module is set.
- app_package.__dict__.setdefault('_MODELS',
[]).append(new_class)
+ # Exclude models where the user has set 'no_table = True'
+ if create_table:
+ app_package.__dict__.setdefault('_MODELS',
[]).append(new_class)

# Cache the app label.
opts.app_label = app_label
@@ -725,6 +732,7 @@
def __repr__(self):
return '<%s object>' % self.__class__.__name__

+
############################################
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
############################################
[ewalstad@pasauran django_src]$ python tests/runtests.py
Running tests with database 'postgresql'
All tests passed.


experiment.py file:
from django.core import meta

class MyBaseClass(meta.Model):
"""This class will not result in any tables being generated by
django"""


created_on = meta.DateTimeField(auto_now_add=True)
modified_on = meta.DateTimeField(auto_now=True)

class META:
no_table = True

class MyDerived(MyBaseClass):
"""This class will have tables generated and will include the:
- created_on and
- modified_on fields
which are inherited from the 'MyBaseClass' class.


"""
name = meta.CharField(maxlength=25)
class META:
module_name = 'my_derived_class'

class MyOtherDerived(MyDerived):
"""This class will not result in any tables being generated by
django and
it will include all the fields inherited from both 'MyBaseClass'
and
'MyDerived' as well as those fields defined here.
"""
color = meta.CharField(maxlength=25)
class META:
module_name = 'my_other_derived'
# Explicitly set the no_table flag so that this class doesn't
result in
# a table being created.
no_table = True

class MyLastDerived(MyOtherDerived):
"""This class will have tables generated and will include the:
- created_on and
- modified_on fields
which are inherited from the 'MyBaseClass' class,
- name
which is inherited from the 'MyDerived' class and
- color
which is inherited from the 'MyOtherDerived' class as well as those
fields
defined here.
"""
size = meta.IntegerField(choices=((0, 'small'),
(1, 'medium'),
(2, 'large'),
))
class META:
module_name = 'my_last_derived'


[ewalstad@pasauran django_src]$ django-admin.py sql experiment
BEGIN;


CREATE TABLE experiment_my_derived_class (
id serial NOT NULL PRIMARY KEY,
modified_on timestamp with time zone NOT NULL,
created_on timestamp with time zone NOT NULL,
name varchar(25) NOT NULL
);

CREATE TABLE experiment_my_last_derived (


id serial NOT NULL PRIMARY KEY,

color varchar(25) NOT NULL,


modified_on timestamp with time zone NOT NULL,
created_on timestamp with time zone NOT NULL,

name varchar(25) NOT NULL,
size integer NOT NULL
);

Eric Walstad

unread,
Sep 19, 2005, 2:07:21 PM9/19/05
to Django users
Hi Adrian,

Please let me know if you are interested in my previous patch for
ticket 419.

To summarize, it modifies django/core/meta/__init__.py to:
1. check for a (new) meta attribute "no_table"
2. if no_table is true, keep the model class out of the module's list
of "_MODEL"s

The result is that the model isn't included in other functions that
operate on meta.MODEL classes while still allowing inheritance.

I've only tested it lightly, with the existing unit tests and my simple
"experiment" classes.

I wasn't able to figure out how to obtain the same results (#2, above)
by creating a meta.AbstractModel class, so I simply added the
"no_table" attribute, which works.

If you like the fix, please tell me how you like your patch submissions
formatted and/or what other test(s) you'd like to see. I'll then clean
it up and write unit tests like the existing ones that generate the
model example documentation.

Eric.

Alberto Valverde

unread,
Sep 19, 2005, 8:08:43 PM9/19/05
to Django users
I've managed to do it my defining the superclass in another module and
importing it from your 'apps/appname/models/appname.py' file. That
other module can live under your models directory, just give it a
different name than appname.py. I guess it works because the dynamic
model "compiler" doesn't treat it like a real model which should make a
table of (the attributes & methods are inherited, though).

Hope it helps,
Alberto

Eric Walstad

unread,
Sep 20, 2005, 7:31:54 PM9/20/05
to Django users
Hi Alberto,
When I tried this, it didn't work. The classes defined in the other
module were subclasses of meta.Model. In other words, I want to have
my base class define database fields that will be inherited by the
derived classes. Can you show an example of how you made it work?

Thanks,

Eric.

Reply all
Reply to author
Forward
0 new messages