Modeling Question on Many-to-Many(with through) and Proxy models

111 views
Skip to first unread message

Arruda

unread,
Apr 21, 2012, 1:35:28 PM4/21/12
to django...@googlegroups.com
I know already that are some limitations when using Many-to-Many and proxy models, I just want to know what is the best practices when doing something in this case:
obs: the Choices is from model_utils (Django model utils)

I have this first class:

class Foo(models.Model):
    "The single table model"
    TYPE_CHOICES = Choices(
                           (0,'foo','Foo'),
                           (1,'foo1','Foo1'),
                           )
    type = models.SmallIntegerField(choices=TYPE_CHOICES,default=0)
    
    
    def __unicode__(self):
        return "%s - %s" %(self.pk,self.type)

Then I have this proxy model
class Foo1(Foo):
        
    objects = TypeAwareManager('type',1)    
    def __cast_from(self,super_instance):
        #change this instance fields for the super_instance one
        #but only the ones that matter.        
        pass
 
    
    def __init__(self, *args, **kwargs):        
        super(Foo1, self).__init__(*args, **kwargs)
        #:cast
        if args != ():
            if isinstance(args[0],Foo):
                self.__cast_from(args[0])
        self.type = 1

    class Meta:
        proxy= True
    
    def save(self, *args, **kwargs):
        self.type = 1
        super(Foo1, self).save(*args, **kwargs)

and it's QueryManager:
class TypeAwareManager(models.Manager):
    
    def __init__(self,type_field, type, *args, **kwargs):
        super(TypeAwareManager, self).__init__(*args, **kwargs)
        self.type = type
        self.type_field = type_field
    
    def get_query_set(self):
        return super(TypeAwareManager, self).get_query_set().filter(**{self.type_field:self.type})

Then I have this other model:
class Bar(models.Model):
    "a class that has connection to other proxies"
    
    
    def __unicode__(self):
        return "%s" %(self.pk)
 
And finally, I have the many-to-many model:

class FooBar(models.Model):
    "many to many foo to bars"
    
    foo = models.ForeignKey('my_app.Foo',related_name="%(class)s_list")
    bar = models.ForeignKey('my_app.Bar',related_name="%(class)s_list")
    
    value = models.SmallIntegerField("Value",default=0)
        
        
    def __unicode__(self):
        return "%s - %s - %s" %(self.pk, self.foo, self.bar)
    
Now the problem:
If I add this field to Bar:
    foos = models.ManyToManyField('my_app.Foo',related_name='bars_list',through='my_app.FooBar')

Then I can do:
f = Foo()
f.save()
 
f1 = Foo1()
f1.save()
f1 = Foo1()
f1.save()

b = Bar()
b.save()
b.foobar_list.create(foo=f)
b.foobar_list.create(foo=f2)

b.foos.all()
[<Foo: 1 - 0>, <Foo: 3 - 1>] 

But I wanted Bar to a many-to-many with Foo1, not Foo.
And make it  through Foo1Bar(another proxy model but for Foo1Bar) so that it could get in the querys a Foo1 object and not a Foo one when doing this:
b.foobar_list.get(pk=1).foo
<Foo1: 1 - 1> instead of <Foo: 1 - 0>

But, when I try to make this many-to-many to Foo1 instead of Foo(been through FooBar or Foo1Bar), I get this error:
Error: One or more models did not validate:
my_app.bar: 'foos' is a manually-defined m2m relation through model Foo1Bar, which does not have foreign keys to Foo1 and Bar

And this makes sense... so my question is about the best approach in this case.


Should I create this many-to-many table as a normal model(no proxy) and set the foo foreingkey to Foo1 instead of Foo?
Or should I use only FooBar as many-to-many and create some specific queries that would convert ALL the returned foo objects as Foo1 instance?

And if I'm going to have not only Foo1 proxy model, but Foo2, Foo3, and Foo4 and maybe Foo5... which approach is the best?


And of course the most important: I need to have this connection from Bar to any of this Foo1,2,3,4 as much clear as possible.


Thanks.
If it's going to help this is the git repo that i'm testing this case:

akaariai

unread,
Apr 21, 2012, 6:04:30 PM4/21/12
to Django users
On Apr 21, 8:35 pm, Arruda <felipe.arruda.pon...@gmail.com> wrote:
> Should I create this many-to-many table as a normal model(no proxy) and set
> the foo foreingkey to Foo1 instead of Foo?
> Or should I use only FooBar as many-to-many and create some specific
> queries that would convert ALL the returned foo objects as Foo1 instance?

I am not sure if I am answering your question... But here is a dirty
hack to try:
qs = SomeModel.objects.all()
qs.model = SomeModelProxy
print list(qs)

You should get proxy model instances from the queryset.

Now, I just quickly tested this a minute ago, and it seems to work in
a very simple cases. The above uses internals of Django's ORM in a way
it was not designed to be used. So mandatory warnings: If anything
breaks, though luck. A minor version upgrade could break your code
without any notice.

So, in the m2m case I guess I would create a property:
class SomeModel:
foos = M2M(Foo)

def _foo1s(self):
qs = self.foos.all()
qs.model = Foo1
return qs
foo1s = property(_foo1s)

The above might just work. Or not. Once again, be very cautious if you
use any of the above.

- Anssi

Arruda

unread,
Apr 22, 2012, 1:10:33 PM4/22/12
to django...@googlegroups.com
Thanks, I saw that .model thing too, but was also unsure about it... I might just test it.

Arruda

unread,
Apr 22, 2012, 2:05:15 PM4/22/12
to django...@googlegroups.com
So using your tip, I've done this:

class Foo1Bar(FooBar):
    
    objects = TypeAwareManager('foo__type',1)
    
    class Meta:
        proxy= True
        
def change_fb(cls):
    old_foo = cls.foo
    
    @property
    def new_foo(self):
        return Foo1(self.old_foo) if self.old_foo != None else None
    
    setattr(cls, 'old_foo', old_foo)
    setattr(cls, 'foo', new_foo)
    
    return cls

This way when calling a Foo1Bar the foo attr would be the cast from Foo to Foo1 if it exist.
And then in Bar I've done what you said:
class Bar(models.Model):
    "a class that has connection to other proxies"
    foos = models.ManyToManyField('my_app.Foo',related_name='bars_list',through='my_app.FooBar')
    
    @property
    def foo1bar_list(self):
        qr = self.foobar_list.filter(foo__type=1)
        qr.model = Foo1Bar
        return qr

    
    def __unicode__(self):
        return "%s" %(self.pk)

And this is aparently working, but I still want to make some more tests.
Reply all
Reply to author
Forward
0 new messages