I have 3 models that I am dealing with here: SurveyQuestion, Update, and Notification. I use a post_save signal to create an instance of the Notification model whenever an instance of SurveyQuestion or Update was created.
The Notification model has a GenericForeignKey which goes to whichever model created it. Inside the Notification model I try to use the ForeignKey to set __str__ as the title field of the instance of the model that created it. Like so:
class Notification(models.Model):
    source_object = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    source = GenericForeignKey("source_object", "object_id")
    #more stuff
    def __str__(self):
        return f'{self.source.title} notification'
I am able to create instances of SurveyQuestion and Update from the admin panel, which is then (supposed to be) creating an instance of Notification. However, when I query instances of Notification in the shell:
from hotline.models import Notification
notifications = Notification.objects.all()
for notification in notifications:
    print (f"Notification object: {notification}")
NoneType
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
      1 for notification in notifications:
----> 2     print (notification)
File ~/ygzsey/hotline/models.py:27, in Notification.__str__(self)
     26 def __str__(self):
---> 27     return f'{self.source.title} notification'
AttributeError: 'NoneType' object has no attribute 'title'
When I query instances of SurveyQuestion:
from hotline.models import SurveyQuestion
surveys = SurveyQuestion.objects.all()
for survey in surveys:
    print (f"Model: {survey.__class__.__name__}")
Model: SurveyQuestion
When I query instances of Notification and try to print the class name of their ForeignKey field (I labled it source), I get this:
for notification in notifications:
    print (f"Notification for {notification.source.__class__.__name__}")
Notification for NoneType
Notification for NoneType
Notification for NoneType
So it seems that the SurveyQuestion, Update, and Notification instances are saving properly, but there is some problem with the GenericForeignKey.
I had the post_save create an instance of Notification using Notification(source_object=instance, start_date=instance.start_date, end_date=instance.end_date), but that would give me an error when trying to save an instance of SurveyQuestion or Update in the admin panel:
ValueError at /admin/hotline/update/add/
Cannot assign "<Update: Update - ad>": "Notification.source_object" must be a "ContentType" instance.
So I changed it to Notification(source_object=ContentType.objects.get_for_model(instance), start_date=instance.start_date, end_date=instance.end_date).
My full models.py:
from django.db import models
from datetime import timedelta
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save
from django.dispatch import receiver
def tmrw():
    return timezone.now() + timedelta(days=1)
class Notification(models.Model):
    source_object = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    source = GenericForeignKey("source_object", "object_id")
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(default=tmrw)
    class Meta:
        verbose_name = 'Notification'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.source.title} notification'
class Update(models.Model):
    title = models.CharField(max_length=25)
    update = models.TextField()
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(default=tmrw)
    #notification = GenericRelation(Notification, related_query_name='notification')
    class Meta:
        verbose_name = 'Update'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.__class__.__name__} - {self.title}'
class SurveyQuestion(models.Model):
    title = models.CharField(max_length=25)
    question = models.TextField()
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(default=tmrw)
    #notification = GenericRelation(Notification, related_query_name='notification')
    class Meta:
        verbose_name = 'Survey'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.__class__.__name__} - {self.title}'
class SurveyOption(models.Model):
    survey = models.ForeignKey(SurveyQuestion, on_delete=models.CASCADE, related_name='options')
    option = models.TextField()
    id = models.AutoField(primary_key=True)
    class Meta:
        verbose_name = 'Survey option'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.survey.title} option #{self.id}'
@receiver(post_save)
def create_notification(instance, **kwargs):
    #"""
    print (f"instance: {instance}")
    print (f"instance.__class__: {instance.__class__}")
    print (f"instance.__class__.__name__: {instance.__class__.__name__}")
    #"""
    senders = ['SurveyQuestion', 'Update']
    if instance.__class__.__name__ in senders:
        notification = Notification(source_object=ContentType.objects.get_for_model(instance), start_date=instance.start_date, end_date=instance.end_date)
        notification.save()
post_save.connect(create_notification)
    
                
I have 3 models that I am dealing with here: SurveyQuestion, Update, and Notification. I use a post_save signal to create an instance of the Notification model whenever an instance of SurveyQuestion or Update was created.
The Notification model has a GenericForeignKey which goes to whichever model created it. Inside the Notification model I try to use the ForeignKey to set __str__ as the title field of the instance of the model that created it. Like so:
class Notification(models.Model):
    source_object = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    source = GenericForeignKey("source_object", "object_id")
    #more stuff
    def __str__(self):
        return f'{self.source.title} notification'
I am able to create instances of SurveyQuestion and Update from the admin panel, which is then (supposed to be) creating an instance of Notification. However, when I query instances of Notification in the shell:
from hotline.models import Notification
notifications = Notification.objects.all()
for notification in notifications:
    print (f"Notification object: {notification}")
NoneType
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
      1 for notification in notifications:
----> 2     print (notification)
File ~/ygzsey/hotline/models.py:27, in Notification.__str__(self)
     26 def __str__(self):
---> 27     return f'{self.source.title} notification'
AttributeError: 'NoneType' object has no attribute 'title'
When I query instances of SurveyQuestion:
from hotline.models import SurveyQuestion
surveys = SurveyQuestion.objects.all()
for survey in surveys:
    print (f"Model: {survey.__class__.__name__}")
Model: SurveyQuestion
When I query instances of Notification and try to print the class name of their ForeignKey field (I labled it source), I get this:
for notification in notifications:
    print (f"Notification for {notification.source.__class__.__name__}")
Notification for NoneType
Notification for NoneType
Notification for NoneType
So it seems that the SurveyQuestion, Update, and Notification instances are saving properly, but there is some problem with the GenericForeignKey.
I had the post_save create an instance of Notification using Notification(source_object=instance, start_date=instance.start_date, end_date=instance.end_date), but that would give me an error when trying to save an instance of SurveyQuestion or Update in the admin panel:
ValueError at /admin/hotline/update/add/
Cannot assign "<Update: Update - ad>": "Notification.source_object" must be a "ContentType" instance.
So I changed it to Notification(source_object=ContentType.objects.get_for_model(instance), start_date=instance.start_date, end_date=instance.end_date).
My full models.py:
from django.db import models
from datetime import timedelta
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save
from django.dispatch import receiver
def tmrw():
    return timezone.now() + timedelta(days=1)
class Notification(models.Model):
    source_object = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    source = GenericForeignKey("source_object", "object_id")
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(default=tmrw)
    class Meta:
        verbose_name = 'Notification'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.source.title} notification'
class Update(models.Model):
    title = models.CharField(max_length=25)
    update = models.TextField()
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(default=tmrw)
    #notification = GenericRelation(Notification, related_query_name='notification')
    class Meta:
        verbose_name = 'Update'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.__class__.__name__} - {self.title}'
class SurveyQuestion(models.Model):
    title = models.CharField(max_length=25)
    question = models.TextField()
    start_date = models.DateTimeField(default=timezone.now)
    end_date = models.DateTimeField(default=tmrw)
    #notification = GenericRelation(Notification, related_query_name='notification')
    class Meta:
        verbose_name = 'Survey'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.__class__.__name__} - {self.title}'
class SurveyOption(models.Model):
    survey = models.ForeignKey(SurveyQuestion, on_delete=models.CASCADE, related_name='options')
    option = models.TextField()
    id = models.AutoField(primary_key=True)
    class Meta:
        verbose_name = 'Survey option'
        verbose_name_plural = f'{verbose_name}s'
    def __str__(self):
        return f'{self.survey.title} option #{self.id}'
@receiver(post_save)
def create_notification(instance, **kwargs):
    #"""
    print (f"instance: {instance}")
    print (f"instance.__class__: {instance.__class__}")
    print (f"instance.__class__.__name__: {instance.__class__.__name__}")
    #"""
    senders = ['SurveyQuestion', 'Update']
    if instance.__class__.__name__ in senders:
        notification = Notification(source_object=ContentType.objects.get_for_model(instance), start_date=instance.start_date, end_date=instance.end_date)
        notification.save()
post_save.connect(create_notification)
    
        
            
                
                    
                    You should use source, not :source_object
Notification(
    source=instance, start_date=instance.start_date, end_date=instance.end_date
)
A GenericForeignKey essentially combines two columns, the source_object (very bad name) that points to the type of the item the GenericForeignKey refers to, and a column that stores the primary key (or another unique column) of that object.
