1

Possible Duplicate:
Polymorphism in Django models

I have a Constraint abstract base model which has a foreign key to a Group. Several models inherit from Constraint to behave in a variety of different ways. Here's a (much simplified) version of what I have:

class Constraint(models.Model):
    group = models.ForeignKey(Group)

    def get_constraint_type(self):
        return 'Base'

    class meta:
        abstract = True

class UserConstraint(Constraint):
    user = models.ForeignKey(User)

    def get_constraint_type(self):
        return 'User'

class ProjectConstraint(Constraint):
    project = models.ForeignKey(Project)

    def get_constraint_type(self):
        return 'Project'

I need to be able to, given a Group, come up with a list of constraint model instances pointing to it.

e.g. if I do

group = ...
constraints = group.constraint_set.all()
for c in constraints:
    print c.get_constrait_type()

right now, it will print 'Base' a bunch of times, rather than 'User', 'Project', 'User', etc.

A really hacky solution would be to implement a function like this in the base class:

def get_child(self):
    try:
        return self.usercontraint
    except UserConstraint.DoesNotExist:
        pass
    try:
        return self.projectcontraint
    except ProjectConstraint.DoesNotExist:
        pass
    # etc...

but that seems really terrible. Do any better solutions exist?

Community
  • 1
  • 1
numegil
  • 1,916
  • 7
  • 26
  • 38

1 Answers1

2

SO, do you mean that you have instance of Group model and you want to have related queryset to UserConstraint or to ProjectConstraint?

There is solution to achieve this with appropriate related_name parameter in ForeignKey field. Details are available in docs.

I think you should define Constraint model like this:

class Constraint(models.Model):
    group = models.ForeignKey(Group, related_name="%(class)s_set")

    def get_constraint_type(self):
        return 'Base'

    class Meta:
        abstract = True

and use it like this:

user_constraints = group.userconstraint_set.all()
project_constraints = group.projectconstraint_set.all()

Edit:

I have changed the related_name from "%(class)s" to "%(class)s_set". The previous value was not working and I don't know why.

  • I want to be able to query all the constraints at once. e.g. group.() should return both user and project constraints. From there, I'll have the same function names within each subclass, so calling c.get_constraint_type() will work regardless of which subclass it's in. – numegil Sep 18 '12 at 20:00
  • Under the hood there have to be two queries into database so it will be difficult to do it with one queryset. If you have a bunch of subclasses, maybe good option is to introspect Constraint to get subclasses with `Constraint.__subclasses__()` method and generate a list of querysets to join. – Przemek Lewandowski Sep 18 '12 at 20:51
  • If you want plural for the related_name, you need to add an extra s at the end: "%(class)ss". – Tiphareth Jul 25 '16 at 09:15