Creating an instance of a subclass when superclass instance already exists

319 views
Skip to first unread message

Matt Gardner

unread,
Jun 22, 2011, 9:45:57 PM6/22/11
to Django users
Hello all,

I have a model hierarchy in my django app, and there is one very
important task that I do not know how to do. I have a bunch of
instances of the superclass (Word is the model name), and on input
from a user I want to create a subclass instance (Verb) from that
superclass instance. For example, I might have code like this:

>>> word = Word.objects.get(pk=[input from user])
>>> verb = Verb.createFromSuperclassInstance(word)
>>> verb.save()

The only problem is that I know of no such
"createFromSuperclassInstance" method, nor where to look for it. I do
know how to do this through the database itself (just add a row to the
verb table with the correct foreign key), but I was hoping django
would provide some nice way to do it without me having to explicitly
write SQL. The Verb class's __init__ method creates a new Word
instance, so that doesn't work, unless there's a way to use it that I
don't know about.

Any help?

Thanks,
Matt

Bill Freeman

unread,
Jun 23, 2011, 10:09:48 AM6/23/11
to django...@googlegroups.com
First, if, despite being related by inheritance, Word and Verb are
separate models, they have separate tables. Then if you do create
a Verb, the Word still exists, unless you delete it. Is that what you
want? If both are to exist, were you hoping that a query for the
same key would still find the Word, or do you want it to find the Verb.
And how do you want to treat words (if text words they are) like "record",
which is a noun (keep a record), a verb (record a performance) and an
adjective (record crowd), at the very least. (Note that the pronunciation
of the verb is different, if you were thinking of storing pronunciation info
in the Word.)

If you have separate models, you could certainly write the method that
initializes a completely separate sub-class instance from the instance
data of the super class instance. You could also do some magic using
the field list available in the manager (which has the problem, IIRC, that
it is not a public interface) to know which data to copy, though I'm of the
explicit is better than implicit camp.

So, I guess that I really don't know enough about your problem to
suggest a detailed technique. I suspect that if it were me, I'd probably
be using a foreign key reference from Verb to Word, rather than
sub-classing.

Bill

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

Matthew Gardner

unread,
Jun 23, 2011, 11:36:10 AM6/23/11
to django...@googlegroups.com
Thanks, Bill, for your comments.

Shortly after I posted here I did a little more digging and found this thread from six months ago that described exactly my problem:


That thread didn't seem to have a good solution, though...

As far as whether I should have done inheritance or composition, that's a fair question that could have gone either way.  But I chose inheritance a while ago, as it seemed to make the most sense for what I am doing, and that's the way the code is written.  At several points in my code, I have blocks that do things like this:

try:
    verb = word.verb
    # do verb-specific stuff
except Verb.DoesNotExist:
    pass

I deal with a collection of words most of the time, and if they are also verbs I add some additional stuff.

At this point, all I really want to do is add a row to the verb table that contains the correct foreign key, without creating a new row in the word table.  And I don't want to write SQL to do it, though if I don't find another solution, that's what I'm going to do, because it's fairly straightforward to just do that.  Also, I want to be able to delete just the verb part, which is just deleting the row in the verb table and all corresponding information (i.e., some other tables reference verb but not word, so on deleting the verb row you want to delete the references to it).

And, now that I look at this, it seems that composition would have simplified those two things, as django makes that nice and easy (with a one-to-one field in Verb).  If I can figure out a nice way to do it while keeping inheritance, it would make my life easier, as I already have a bunch of code written and databases that I don't want to repair that use inheritance.  But, given that it looks like composition would probably be easier, maybe I should just take the hit and convert everything?  Thoughts?

Tom Evans

unread,
Jun 23, 2011, 11:47:24 AM6/23/11
to django...@googlegroups.com
On Thu, Jun 23, 2011 at 4:36 PM, Matthew Gardner <mj...@byu.edu> wrote:
> At this point, all I really want to do is add a row to the verb table that
> contains the correct foreign key, without creating a new row in the word
> table.

Verb.objects.create(word=word) doesn't fulfill these criteria?

Cheers

Tom

Matthew Gardner

unread,
Jun 23, 2011, 12:37:36 PM6/23/11
to django...@googlegroups.com
When I run that, I get "TypeError: 'word' is an invalid keyword argument for this function."  So, nope, unless I'm doing something wrong.  Note that Verb inherits from Word, so there is no explicit "word" field in Verb.

Bill Freeman

unread,
Jun 23, 2011, 1:59:04 PM6/23/11
to django...@googlegroups.com
I'm still confused about what you have (what form of inheritance you are using.

In your database, are there both a Word table and a Verb table? Or is
there just
a (set of) Verb (and Noun and Adjective, etc.) table(s), derived from
a non-Model
Word class?

If there's a Noun instance "record" and a Verb instance "record", do they share
any data (such that if you change it in one it is changed in the
other) or do they
just have duplicate copies of some data?

Bill

Matthew Gardner

unread,
Jun 23, 2011, 2:18:13 PM6/23/11
to django...@googlegroups.com
I'm using django's model inheritance.  Sorry I wasn't clear.  So there's a Word model and a Verb model.

class Word(models.Model):
    # Word stuff

class Verb(Word):
    # Verb-specific stuff

The way django handles this is to create a word table and a verb table.  The verb table has a reference to the word instance in the word table, plus any other verb-specific information.  I think the database is essentially the same as this:

class Verb(models.Model):
    word = models.ForeignKey('Word')
    # Verb-specific stuff

But you're dealing with inheritance in python instead of composition.

Matthew Gardner

unread,
Jun 23, 2011, 2:22:35 PM6/23/11
to django...@googlegroups.com
On Thu, Jun 23, 2011 at 2:18 PM, Matthew Gardner <mj...@byu.edu> wrote:
class Verb(models.Model):
    word = models.ForeignKey('Word')
    # Verb-specific stuff

Sorry, this is more accurate:

class Verb(models.Model):
    word = models.OneToOneField('Word')
    # verb-specific stuff 

Bill Freeman

unread,
Jun 23, 2011, 3:47:45 PM6/23/11
to django...@googlegroups.com
So, we're talking about "Multi-table inheritance"
(https://docs.djangoproject.com/en/1.3/topics/db/models/#multi-table-inheritance).
Note that this uses "an automatically-created OneToOneField".

What you really want to do is to create a python instance of the Verb
class. It's id (in the Verb table) will be empty, meaning the Verb
row will get created on save(). If the field that Verb uses to refer
to Word is already set at save() time, the ORM *MIGHT* just happily
save Verb, and either not save the unmodified Word (I'm pretty sure
that you must have an instantiation of the Word instance lying
around), or, at worst, just update it with unchanged data, rather than
creating a new instance. You'll have to experiment, unless an ORM
expert speaks up.

One problem is how to set that reference in the Verb object. I don't
think you get a 'word' attribute on a Verb object, do you? If worst
comes to worse you can access the attribute by using
v.__dict__['attribute_name'] where v is a Verb instance. You can poke
around in pdb on a Verb instance that was made and already saved in
the normal way to see how things are named and represented.

You are probably on your own to assure that you don't add more than
one Verb to a given Word. Some of the reverse lookup stuff may get
confused if there are more than one. I doubt that the uniqueness
constraint in the DB functions in that direction. Beware multiple
threads - you may need to play with transactions and roll backs.

And, of course, things could change in a future version even if this works now.

Good luck, Bill

Matthew Gardner

unread,
Jun 23, 2011, 8:44:12 PM6/23/11
to django...@googlegroups.com
Thanks for your input, Bill, I appreciate it.  I think I'm just going to take a hit and switch to an explicit OneToOneField instead of trying to make this work with inheritance.  The other way things will just work, because django's built to make it work, but doing it with inheritance is difficult and ugly.  Thanks for helping me see that.  I wonder if it's worth putting a note in the django docs that in some cases a OneToOneField is more appropriate than inheritance, given some of these problems (and the fact that other people have had the same problem, as I noted with my link to a previous post to django-users).  Anyway, thanks again for your help.

Matt

Bill Freeman

unread,
Jun 24, 2011, 10:15:10 AM6/24/11
to django...@googlegroups.com
So long as it *IS* one to one. I.e.; there are no Word-s that are both a
Verb and a Noun.

I'd sill go with a foreign key field in Verb (and Noun, etc.)
referring to Word. I
think that you can even mark it unique, meaning that only one Verb can
refer to any given Word, but both a Noun and a Verb could refer to the same
Word. But you know your needs.

Bill

Tom Evans

unread,
Jun 24, 2011, 10:25:01 AM6/24/11
to django...@googlegroups.com
On Fri, Jun 24, 2011 at 3:15 PM, Bill Freeman <ke1...@gmail.com> wrote:
> So long as it *IS* one to one.  I.e.; there are no Word-s that are both a
> Verb and a Noun.
>
> I'd sill go with a foreign key field in Verb (and Noun, etc.)
> referring to Word.  I
> think that you can even mark it unique, meaning that only one Verb can
> refer to any given Word, but both a Noun and a Verb could refer to the same
> Word.  But you know your needs.
>
> Bill
>

There is no issue with Word-s that are both Noun and Verb if both Noun
and Verb have a OneToOneField to Word - the Noun and Verb instances
can point to the same Word instance without any issues whatsoever.

Cheers

Tom

Bill Freeman

unread,
Jun 24, 2011, 10:37:12 AM6/24/11
to django...@googlegroups.com
Sorry. I'd been thinking in terms of a not null constraint and confusing myself
that it would apply in both directions.

Bill

Reply all
Reply to author
Forward
0 new messages