Correctly using subclasses/proxies in django that have self referential foreign keys?

310 views Asked by At

I have two classes, with a super class. In essence the two classes are concrete classes on a tree. One is a leaf, one is a branch. They share properties defined in the super class.

None of the below classes are finished. I've tried both making the superclass abstract, and the subclasses proxies. Hopefully the code below explains what I'm trying to achieve.

This is the 'super class'

class Owner(models.Model):
    name = models.CharField(max_length=200)
    def __unicode__(self):
        return self.name
    class Meta:
        abstract=True

This is the 'leaf'

class User(Owner):
    pass

This is the 'branch'.

class Group(Owner):
    head = models.ForeignKey(User)
    members = models.ManyToManyField(Owner,through='Membership')

This shows how a user can belong to a group by a membership.

class Membership(models.Model):
    date_joined = models.DateField()
    user = models.ForeignKey(Owner)
    group = models.ForeignKey(Group)

My restrictions are that each user can belong to many groups (via the linker Membership). Each group can be a member of a single group.

This fails because I'm referencing Owner in both the membership as the user, and in the group members. I feel like this is the sort of thing I could solve with generics in Java, but thats not going to help me here.

I've also seen ContentTypes used for this sort of thing, but they seem too complicated for what I'm trying to do. Am I wrong? I can't figure out how to apply that paradigm to my example. I also found this question but I'm still not certain on how it should be implemented.

1

There are 1 answers

1
dgel On BEST ANSWER

You can't have foreign key fields pointing to an abstract class (there is no table in the DB for an abstract class).

You'll probably need to implement self-referential foreign key for each Group to belong to zero or one group. Something like this:

class Base(models.Model):
    name = models.CharField(max_length=200)
    def __unicode__(self):
        return self.name
    class Meta:
        abstract=True


class User(Base):
    groups = models.ManyToManyField('Group', through='Membership', related_name='members')


class Group(Base):
    head = models.ForeignKey(User)
    parent = models.ForeignKey('self', blank=True, null=True, related_name='children')

    def descendants(self, **kwargs):
        qs = self.children_set.filter(**kwargs)
        for group in self.children_set.all():
            qs = qs | group.descendants(**kwargs)
        return qs


class Membership(models.Model):
    date_joined = models.DateField()
    user = models.ForeignKey(User)
    group = models.ForeignKey(Group)

The Base class above does nothing other than dictate that each inherited class has a name field in their respective DB table and __unicode__ method- nothing more. You could simply copy and paste the name field and __unicode__ method into User and Group and it would be functionally identical. Having a common abstract parent doesn't create any DB relationships and you can't use it to query the DB.

I can't really see any reason for using proxy classes in this situation.