[Fixed]-Reversing a unique generic foreign key (and returning an object as opposed to a related manager)

13👍

Well, looking at the question in Chris’s comment helped, and I wrote a mixin to return the object using a quick lookup (which should be cached):

class ContactMixin(object):
    @property
    def contactcard(self):
        ctype = ContentType.objects.get_for_model(self.__class__)
        try:
            contact = Contact.objects.get(content_type__pk = ctype.id, object_id=self.id)
        except Contact.DoesNotExist:
            return None 
        return contact

and the Person:

 class Person(ContactMixin, models.Model):
     ...

now I can just call

myperson.contactcard.phone_number 

I won’t accept just yet, as there might be other suggestions

5👍

if a queryset contains exactly one element you can do qs.get(), or in your case

person.contact.get()

(you may still need to catch the DoesNotExists exception)

👤second

4👍

The accepted answer above implicitly assumes that the contact field is nullable whereas in the original post this is not the case. Assuming that you do want the key to be nullable I would do this:

class Contact(models.Model):
    ...
    content_type = models.ForeignKey(
        ContentType,
        blank=True,
        null=True,
        on_delete=models.SET_NULL
    )
    object_id = models.PositiveIntegerField(blank=True, null=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together = ('content_type', 'object_id',)


class Person(models.Model):
    ...
    contacts = generic.GenericRelation(Contact)

    @property
    def contact(self):
       return self.contacts.first()


# Or if you want to generalise the same thing to a mixin.
class ContactableModel(models.Model):
    contacts = generic.GenericRelation(Contact)

    class Meta:
        abstract = True

    @property
    def contact(self):
       return self.contacts.first()

If there is no contact then .first() will return None. Simple and pythonic. There’s no need for all the generic introspection mechanics of the accepted answer, the references are already right at our fingertips.

👤jhrr

Leave a comment