2

I have a django model that I which to subclass that has a generic relationship attached that I wish to subclass:

class Person(models.Model):
    name = models.CharField(max_length=255)
    contact_details = generic.GenericRelation('ContactDetail')

class Teacher(Person):
    field_of_study = models.CharField(max_length=255,default="Underwater Basket-weaving")

class ContactDetail(models.Model):
    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.CharField(blank=True, null=True, max_length=256)
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    value = models.CharField(max_length=256)

For clarity, I have to use a generic relation as department can also have contact details.

class Department(models.Model):
    contact_details = generic.GenericRelation('ContactDetail')

When I create a person, I can make and get their contact details like so:

> alice = Person(name="Alice")
> ContactDetail(content_object=alice,value="555-1000")
> print alice.contact_details
[ ... list of things ... ]

However, when I make a teacher this happens:

> bob = Teacher(name="Bob")
> ContactDetail(content_object=bob,value="555-2000")
> print bob.contact_details
[ ... list of things ... ]
> bob_person = Person.get(name="Bob") # Returns the person instance of Bob
> print bob_person.contact_details
[]

Nothing is returned!

What I want is for a teachers contact details to be stored against the Person objects and not the Teacher objects. How can I do this?

1 Answers1

0

Ok so after getting half way done I realised this was leading me down a giant boondoogle with another app. So below is a half way solution that might help someone else.

We make use of djangos very internal get_parent_list to get the parent models and operate on those:

@receiver(pre_save, sender=ContactDetail)
def set_object_to_super(sender, **kwargs):
    obj = kwargs['instance']
    c_obj = obj.content_object
    parents = c_obj._meta.get_parent_list() # Beware: This is a set, not a list!

    if len(parents) == 0:
        # If no parents, its a base class
        return
    elif len(parents) == 1:
        # If only one parent, get that from the object
        parent = list(parents)[0]
        obj.content_object = getattr(c_obj, '%s_ptr'%parent._meta.model_name)
    else:
        # Here's where it gets tricky, there are two or more base classes
        # You, dear reader, will need to figure out which to use!
        # Remember, you can only have one.
        pass

Why is this a half solution? Well, this will appropriately save the GenericRelation against the parent class, so this will work:

> bob = Teacher(name="Bob")
> ContactDetail(content_object=bob,value="555-2000")
> bob_person = Person.get(name="Bob") # Returns the person instance of Bob
> print bob_person.contact_details
[ ... list of things ... ]

But, we've just messed things around so the problem is now backwards! Because if we try and get bobs details there are none!

> print bob.contact_details
[]

As I said, massive boondoggle. So if you finish this code, I'll gladly accept an edit.

Community
  • 1
  • 1