views:

65

answers:

2

This is somewhat related to the question posed in this question but I'm trying to do this with an abstract base class.

For the purposes of this example lets use these models:

class Comic(models.Model):
    name = models.CharField(max_length=20)
    desc = models.CharField(max_length=100)
    volume = models.IntegerField()
    ... <50 other things that make up a Comic>

    class Meta:
        abstract = True

class InkedComic(Comic):
    lines = models.IntegerField()

class ColoredComic(Comic):
    colored = models.BooleanField(default=False)

In the view lets say we get a reference to an InkedComic id since the tracer, err I mean, inker is done drawing the lines and it's time to add color. Once the view has added all the color we want to save a ColoredComic to the db.

Obviously we could do

inked = InkedComic.object.get(pk=ink_id)
colored = ColoredComic()
colored.name = inked.name
etc, etc.

But really it'd be nice to do:

colored = ColoredComic(inked_comic=inked)
colored.colored = True
colored.save()

I tried to do

class ColoredComic(Comic):
    colored = models.BooleanField(default=False)

    def __init__(self, inked_comic = False, *args, **kwargs):
        super(ColoredComic, self).__init__(*args, **kwargs)
        if inked_comic:
            self.__dict__.update(inked_comic.__dict__)
            self.__dict__.update({'id': None}) # Remove pk field value

but it turns out the ColoredComic.objects.get(pk=1) call sticks the pk into the inked_comic keyword, which is obviously not intended. (and actually results in a int does not have a dict exception)

My brain is fried at this point, am I missing something obvious, or is there a better way to do this?

+1  A: 

What about a static method on the class to handle this?

colored = ColoredComic.create_from_Inked(pk=ink_id)
colored.colored = True
colored.save()

Untested, but something to this effect (using your code from above)

class ColoredComic(Comic):
    colored = models.BooleanField(default=False)

    @staticmethod
    def create_from_Inked(**kwargs):
        inked = InkedComic.objects.get(**kwargs)
        if inked:
            colored = ColoredComic.objects.create()
            colored.__dict__.update(inked.__dict__)
            colored.__dict__.update({'id': None}) # Remove pk field value
            return colored
        else:
            # or throw an exception...
            return None
T. Stone
Testing this, the only thing I needed to change was ColoredComic.objects.create() to ColoredComic() since I only wanted to deal with a local copy of that model at that point (more business logic is needed before saving in my case.)I can now get back to work! Thanks!
JT
A: 

For that simple case, this will work:

inked = InkedComic.object.get(pk=ink_id)
inked.__class__ = ColoredComic
inked.colored = True
inked.save()
Clément
I actually started with something like this, but if you change the class like this you don't get all the ColoredComic attributes on the inked item, so the save() fails. In the example you got around that by doing inked.colored = True, but this becomes a pain as the number of child class attributes grows. (my actual problem has half a dozen extra attrs, most of which are either defaulted or optional)But yes for the simple example given this does in fact work :)
JT