Andrew
Andrew

Reputation: 1040

Get attribute of a generic foreign key

I'm still trying to wrap my head around generic foreign keys in Django, so what I've come up with is quite basic so far. I'm trying to create a NotificationRecipient, which has a generic foreign key of 2 different models I've already created. These notification recipients can either be a Client or an Account. I may decide to add more recipients of a new model.

I want to create a get_email method in NotificationRecipient, where it checks if the recipient is a contact, client, or account. Then depending on which model it is, it pulls a different attribute.

My existing models look a little like this:

class Client(models.Model):
    primary_email = models.EmailField(blank=True)
    ...

class Account(AbstractNamedUser):
    email = models.EmailField(blank=True)
    ...

Try to get the email depending on the model:

class NotificationRecipient(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def get_email_addr(self):
        ''' Gets the model, then pulls the email address. '''
        # Get the model
        # if client:
            # return primary_email
        # elif account:
            # return email

How would I go about doing it?

Upvotes: 1

Views: 579

Answers (2)

Jon Loo
Jon Loo

Reputation: 121

It is best to define a method on your targeted models rather than building all the business logic in your NotificationRecipient model.

The logic is that the NotificationRecipient model only needs to know that it requires an email address.

class Client(...):
    def get_email_addr(self):
        return primary_email

class Account(...):
    def get_email_addr(self):
        return email

class NotificationRecipient(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def get_email_addr(self):

        try:
            email = self.content_object.get_email_addr()

        # if you want to enforce the attribute
        except AttributeError:
            raise ImproperlyConfigured('Model requires an email address')

        # if you need a default
        if not email:
            return '[email protected]'

        return email

Upvotes: 1

Daniel Roseman
Daniel Roseman

Reputation: 599778

You can check the content_type field to determine which kind the object is.

But rather than checking the type, you might consider defining an attribute on all the target models which returns the relevant attribute.

Upvotes: 1

Related Questions