turning a Model instance into an instance of one of it's subclasses

49 views
Skip to first unread message

morgan wahl

unread,
Dec 20, 2010, 2:29:24 PM12/20/10
to Django users
Hello all,

I was wonder if there is a way to turn a model instance into an
instance of a subclass of it's class. E.g.:

class Document(models.Model):
doctype = models.CharField(max_length=256, blank=True, null=True)

class UploadedFile(Document):
filename = models.CharField(max_length=256)

Given a Document that isn't already an UploadedFile, can I turn it
into one? I know at the database level it's just a matter of inserting
a new row into the uploadedfile table with document_ptr_id = the
Document's id. I've tried

doc = Document.objects.create(doctype='photo')
uf = UploadedFile(
document_ptr=doc,
filename='test.jpg',
)
uf.save()

But that seems to produce two separate instances (i.e. uf.doctype !=
doc.doctype).

Is there a way to do this?

Thanks in advance.
-Morgan Wahl

Marc Aymerich

unread,
Dec 20, 2010, 4:49:54 PM12/20/10
to django...@googlegroups.com
On Mon, Dec 20, 2010 at 8:29 PM, morgan wahl <morgy...@gmail.com> wrote:
Hello all,

I was wonder if there is a way to turn a model instance into an
instance of a subclass of it's class. E.g.:

class Document(models.Model):
   doctype = models.CharField(max_length=256, blank=True, null=True)

class UploadedFile(Document):
   filename = models.CharField(max_length=256)

Given a Document that isn't already an UploadedFile, can I turn it
into one? I know at the database level it's just a matter of inserting
a new row into the uploadedfile table with document_ptr_id = the
Document's id. I've tried


I think that something like this should do the work:

Document.UploadFile.filename="rat.doc"
Document.save() 


--
Marc

Morgan Wahl

unread,
Dec 20, 2010, 5:00:57 PM12/20/10
to django...@googlegroups.com
Um, I'm not sure what it is you're trying to demonstrate here... in my
example bits of code Document and UploadedFile are Model classes, not
instances.

>
> --
> 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.
>

Marc Aymerich

unread,
Dec 20, 2010, 5:04:08 PM12/20/10
to django...@googlegroups.com
On Mon, Dec 20, 2010 at 11:00 PM, Morgan Wahl <morgy...@gmail.com> wrote:
On Mon, Dec 20, 2010 at 4:49 PM, Marc Aymerich <glic...@gmail.com> wrote:
>
>
> On Mon, Dec 20, 2010 at 8:29 PM, morgan wahl <morgy...@gmail.com> wrote:
>>
>> Hello all,
>>
>> I was wonder if there is a way to turn a model instance into an
>> instance of a subclass of it's class. E.g.:
>>
>> class Document(models.Model):
>>    doctype = models.CharField(max_length=256, blank=True, null=True)
>>
>> class UploadedFile(Document):
>>    filename = models.CharField(max_length=256)
>>
>> Given a Document that isn't already an UploadedFile, can I turn it
>> into one? I know at the database level it's just a matter of inserting
>> a new row into the uploadedfile table with document_ptr_id = the
>> Document's id. I've tried
>>
>
> I think that something like this should do the work:
> Document.UploadFile.filename="rat.doc"
> Document.save()
>
> --
> Marc
Um, I'm not sure what it is you're trying to demonstrate here... in my
example bits of code Document and UploadedFile are Model classes, not
instances.


Hi Morgan, sorry, Yes you're rigth,


d=Document(doctype='whatever')
d.save()
u=UploadFile(document_ptr=d, filename='rat.doc')
u.save()


sorry for the confusion here :S

--
Marc
Message has been deleted

morgan wahl

unread,
Dec 20, 2010, 5:32:00 PM12/20/10
to django...@googlegroups.com
Yes, I had hoped that would work, but it doesn't (see my original post). In your example u.doctype would end up as None instead of 'whatever' (however, u.document_ptr is d).

Marc Aymerich

unread,
Dec 20, 2010, 5:34:16 PM12/20/10
to django...@googlegroups.com


On Mon, Dec 20, 2010 at 11:14 PM, morgan wahl <morgy...@gmail.com> wrote:
Yes, I had hope that would work, but it doesn't (see my original post). In your example u.doctype would end up as None instead of 'whatever'. 

woww, I really need go to sleep :)

I don't know the best way to achieve that, btw you can pass the document attributes to the uploadfile constructor.

d=Document(doctype='whatever')
d.save()
u=UploadFile(document_ptr=d, doctype=d.doctype, filename='rat.doc')
u.save()

 

--
Marc

morgan wahl

unread,
Dec 20, 2010, 5:41:18 PM12/20/10
to Django users
Hmmm, I seem to have somewhat figured out what's going on.

Using Marc's example, when you instantiate u it's doctype field is set
to the default for Documents (None), since it isn't passed in. Then
when you save it, it overwrites the field in d.

So it seems the answer to my question is something like:

d = Document(doctype='whatever')
d.save()
u_kwargs = <dictionary of the fields of d>
u_kwargs.update({
'document_ptr_id': d,
'filename': 'rat.doc',
})
u = UploadedFile(**u_kwargs)
u.save()

Marc Aymerich

unread,
Dec 20, 2010, 5:41:33 PM12/20/10
to django...@googlegroups.com
Morgan, take a look at the inheritanceManager of this app: https://github.com/carljm/django-model-utils#readme 

--
Marc

morgan wahl

unread,
Dec 20, 2010, 5:42:45 PM12/20/10
to Django users
ah, yes you beat me to it while I was composing my post below...

Thanks!

On Dec 20, 5:34 pm, Marc Aymerich <glicer...@gmail.com> wrote:

Łukasz Rekucki

unread,
Dec 20, 2010, 5:52:06 PM12/20/10
to django...@googlegroups.com
This looks a lot like this bug: http://code.djangoproject.com/ticket/7623.

On 20 December 2010 23:14, morgan wahl <morgy...@gmail.com> wrote:
> Yes, I had hope that would work, but it doesn't (see my original post). In
> your example u.doctype would end up as None instead of 'whatever'.
>

> --
> 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.
>

--
Łukasz Rekucki

Morgan Wahl

unread,
Dec 20, 2010, 5:54:41 PM12/20/10
to django...@googlegroups.com
>
> Morgan, take a look at the inheritanceManager of this
> app: https://github.com/carljm/django-model-utils#readme
> --
> Marc
>

I'm not sure how InheritanceManager would quite solve _this_ problem,
although it certainly is handy (I've independently implemented
something like InheritanceCastModel already). So, thanks for the tip!

Just to clarify:
My problem is that Document instances get created at some point (and
referenced by other models), and at some later date a Document might
need to be transmuted to a UploadedFile (which should be fine
hypothetically).

The actual classes I'm dealing with are a little more complicated, my
example was just for this question.

-Morgan

Morgan Wahl

unread,
Dec 20, 2010, 6:06:53 PM12/20/10
to django...@googlegroups.com
That bug is slightly different; it concerns having _more than one_
entry in subclass(es)'s tables refer to the _same_ entry in the
superclass's (which pretty much breaks the analogy with class
inheritance, if you ask me).

My situation is that I have various instances of Document, some of
which are also instances of UploadedFile (or other Document
subclasses). I would like to turn ones which _aren't_ already
UploadedFile instances into ones that are, without changing their
existing fields (especially their IDs). I'm not trying to break the
one-to-one relationship between the tables in multi-table inheritance.

2010/12/20 Łukasz Rekucki <lrek...@gmail.com>:

Andrejus

unread,
Dec 20, 2010, 6:08:59 PM12/20/10
to django...@googlegroups.com

Sent from my mobile device

Morgan Wahl

unread,
Dec 20, 2010, 6:18:06 PM12/20/10
to django...@googlegroups.com
Actually, it's funny you mention #7623; my situation _is_ described in
#11618 ( http://code.djangoproject.com/ticket/11618 ). That bug was
marked as a dup of #7623, but in my opinion isn't.

2010/12/20 Łukasz Rekucki <lrek...@gmail.com>:

Łukasz Rekucki

unread,
Dec 20, 2010, 6:25:02 PM12/20/10
to django...@googlegroups.com
On 21 December 2010 00:18, Morgan Wahl <morgy...@gmail.com> wrote:
> Actually, it's funny you mention #7623; my situation _is_ described in
> #11618 ( http://code.djangoproject.com/ticket/11618 ). That bug was
> marked as a dup of #7623, but in my opinion isn't.

It is a duplicate, that's why I mentioned #7623 which is tracking this
bug. Existence of another table is irrelevant here, IMHO. The main
scheme is the same: You have an instance of class A and you want to
create an instance of class B which is a subclass of A, by writing:

a = A.object.get(pk=1) # existing instance of A
b = B(parent=A)
b.save() # this will fail

The patch on #7623 is describing exactly this situation, so you should
check it out.

Torsten Bronger

unread,
Dec 21, 2010, 7:33:49 AM12/21/10
to django...@googlegroups.com
Hall�chen!

Marc Aymerich writes:

> [...]


>
> Morgan, take a look at the inheritanceManager of this app:
> https://github.com/carljm/django-model-utils#readme

Is anything like this planned for core Django? It's the third or
forth time I see someone needing it.

Tsch�,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Russell Keith-Magee

unread,
Dec 21, 2010, 8:23:46 AM12/21/10
to django...@googlegroups.com
On Tue, Dec 21, 2010 at 8:33 PM, Torsten Bronger
<bro...@physik.rwth-aachen.de> wrote:
> Hallöchen!

>
> Marc Aymerich writes:
>
>> [...]
>>
>> Morgan, take a look at the inheritanceManager of this app:
>> https://github.com/carljm/django-model-utils#readme
>
> Is anything like this planned for core Django?  It's the third or
> forth time I see someone needing it.

It's not planned for 1.3. Whether it is planned for 1.4 will depend
entirely on whether someone drives the issue.

For the record, the use case you describe was considered at the time
model inheritance was introduced. If you search the archives for
"CORBA narrowing" you should be able to find the thread where Malcolm
and I discussed it.

At the time, we decided to omit support for 'narrowing' (to use the
CORBA parlance) as part of the default feature set because it requires
either:
a) table overhead to store the type of the child instance
b) an expensive query (or list of queries) that isn't obviously expensive.

We weren't (and I'm still not) willing to impose either of these
overheads by default.

The good news is that either strategy is easy to implement without
modifications to core. I've got several classes in production that do
narrowing-like behavior (for the record, both using strategy (a)).
With a bit of extra effort, this should be easy to turn into a mixin
that could be shared as part of a third-party support library, much as
django-treebeard or django-mptt provide model support extensions
implementing tree-like structures.

Yours,
Russ Magee %-)

Torsten Bronger

unread,
Dec 21, 2010, 8:58:12 AM12/21/10
to django...@googlegroups.com
Hall�chen!

Russell Keith-Magee writes:

> On Tue, Dec 21, 2010 at 8:33 PM, Torsten Bronger
> <bro...@physik.rwth-aachen.de> wrote:

>> Hall�chen!


>>
>> Marc Aymerich writes:
>>
>>> [...]
>>>
>>> Morgan, take a look at the inheritanceManager of this app:
>>> https://github.com/carljm/django-model-utils#readme
>>
>> Is anything like this planned for core Django? �It's the third or
>> forth time I see someone needing it.
>

> [...]


>
> At the time, we decided to omit support for 'narrowing' (to use
> the CORBA parlance) as part of the default feature set because it
> requires either:
>
> a) table overhead to store the type of the child instance
> b) an expensive query (or list of queries) that isn't obviously expensive.
>
> We weren't (and I'm still not) willing to impose either of these
> overheads by default.

I must clarify my use of "core Django" above. Of course this may
well be realised in contrib/ as an optional feature. However, I'd
love to see it in the Django release with explanations in the Django
docs.

> The good news is that either strategy is easy to implement without
> modifications to core. I've got several classes in production that do
> narrowing-like behavior (for the record, both using strategy (a)).

(a) is the way to go in my opinion. I think the table overhead
itself is not so much of a problem but any migration of old
databases necessary due to it. So, I understand that it cannot be
default behaviour.

> With a bit of extra effort, this should be easy to turn into a mixin
> that could be shared as part of a third-party support library, much as
> django-treebeard or django-mptt provide model support extensions
> implementing tree-like structures.

As somebody who has strugged with this problem for a along time and
having seen a couple of solutions along the road, I find
https://github.com/carljm/django-model-utils quite appealing and
finished.

Morgan Wahl

unread,
Dec 21, 2010, 10:14:49 AM12/21/10
to django...@googlegroups.com
2010/12/20 Łukasz Rekucki <lrek...@gmail.com>:

> On 21 December 2010 00:18, Morgan Wahl <morgy...@gmail.com> wrote:
>> Actually, it's funny you mention #7623; my situation _is_ described in
>> #11618 ( http://code.djangoproject.com/ticket/11618 ). That bug was
>> marked as a dup of #7623, but in my opinion isn't.
>
> It is a duplicate, that's why I mentioned #7623 which is tracking this
> bug. Existence of another table is irrelevant here, IMHO. The main
> scheme is the same: You have an instance of class A and you want to
> create an instance of class B which is a subclass of A, by writing:
>
> a = A.object.get(pk=1) # existing instance of A
> b = B(parent=A)
> b.save() # this will fail
>
> The patch on #7623 is describing exactly this situation, so you should
> check it out.
>
Actually, the code you give doesn't fail. It merely overwrites the
fields in 'a' with the ones given on 'b's instantiation. Which is not
quite what I expected, but does make sense now that I think about it
(since when you save a new instance, you don't leave any field
_unspecified_, it's just that some fields have defaults so you don't
have to mention their values).

#7623's description is something a bit more broad. In terms of your
example, if C is also a subclass of A and b an instance of B, it's
proposing

c = C(parent=b)

presumably the resulting 'c' instance would have all the fields of A
and C and it would share it's A and fields with the 'b' instance. Thus
if A has a string field called 'name' and you do:

c.name = 'someting a rather'

It would effectively set b.name as well (since they would both be
backed by the same row in the table corresponding to A). This is not
how class inheritance typically behaves, and I think it breaks the
analogy Django's ORM makes between it's tables in the database and
Python objects. (Now that I think about my example may have as well,
just a bit more subtly.)

Reply all
Reply to author
Forward
0 new messages