2

I have an "Entry" model, containing one GenericForeignKey:

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

And diffentent content models like "Note" and "File":

class Note(models.Model):
    title = models.CharField(max_length=255)
    text = models.TextField()

class File(models.Model):
    title = models.CharField(max_length=255)
    file_field = models.FileField('File', upload_to='files')
    description = models.TextField(blank=True)

If they are stored, a new "Entry" is automatically created, so I can list all contents by:

Entry.objects.all()

Now I'm searching a way to write a view and a template, which should list all Entrys AND their details, in arbitrary order (for example by date). How can this be done?

Is it possible to associate partial templates to specific models? How does the template know how to display a specific instance within a list?

The whole and not simplified models.py looks like this:

    from django.db.models.signals import post_save, pre_delete

import datetime

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

    def title(self):
        return self.content_object.title

    def __unicode__(self):
        return self.title()

class DateModel(models.Model):
    """ A model for basic date awareness """
    modified_date = models.DateTimeField(blank=True, null=True)
    created_date = models.DateTimeField(blank=True)

    class Meta:
        abstract = True
        ordering = ['-created_date']

    def save(self, *args, **kwargs):
        if not self.pk:
            self.created_date = datetime.datetime.now()
        else:
                self.modified_date = datetime.datetime.now()
        return super(DateModel, self).save(*args, **kwargs)

class Tag(DateModel):
    name = models.CharField(max_length=51, unique=True)

    def __unicode__(self):
        return self.name


class Notebook(models.Model):
    name = models.CharField(max_length=50, unique=True)
    description = models.TextField(blank=True)

    def __unicode__(self):
        return self.name

class EntryModel(DateModel):
    """All Entry models are inherited from this abstract class"""
    title = models.CharField(max_length=255)
    notebook = models.ForeignKey(Notebook, blank=True, null=True)
    tags = models.ManyToManyField(Tag, blank=True, null=True)

    class Meta:
        abstract = True

    def __unicode__(self):
        return self.title

    ######## Begin: Entry Classes ########

class Memo(EntryModel):
    text = models.TextField()

    def save(self, *args, **kwargs):
        if self.title == '-' or self.title == '':
            if len(self.text) > 20:
                self.title = self.text[:17]+'...'
            else:
                self.title = self.text
        return super(EntryModel, self).save(*args, **kwargs)

class File(EntryModel):
    file_field = models.FileField('File', upload_to='files')
    description = models.TextField(blank=True)

    ######## End: Entry Classes ########

def create_entry(sender, **kwargs):
    """Handler for post_save signal to create new Entry"""
    if 'created' in kwargs:
        if kwargs['created']:
            instance = kwargs['instance']
            ctype = ContentType.objects.get_for_model(instance)
            entry = Entry.objects.get_or_create(
                content_type=ctype,
                object_id=instance.id)

def delete_entry(sender, **kwargs):
    """Handler for pre_delete signal to delete related Entry"""
    instance = kwargs['instance']
    ctype = ContentType.objects.get_for_model(instance)
    entry = Entry.objects.get(
        content_type=ctype,
        object_id=instance.id)
    entry.delete()

    # Connect handler to post_save signal
entry_classes = EntryModel.__subclasses__()
for cls in entry_classes:
    post_save.connect(create_entry, sender=cls)
    pre_delete.connect(delete_entry, sender=cls)
Ctrlspc
  • 1,596
  • 1
  • 15
  • 22
Jonas
  • 23
  • 4
  • just to understand your use case: Your entry is either a note or a file? Is this correct? Or do you want to have one entry with multiple notes and files associated to it? – Thomas Kremmel Sep 26 '12 at 17:11
  • yes, my entry is either a note or a file. – Jonas Sep 26 '12 at 17:13

1 Answers1

0

As mentioned in your comment, your entry is either a note or a file. Therefore IMO model inheritance suits better your use case. Your models.py would look like this:

models.py from django.contrib.contenttypes.models import ContentType

class Entry(models.Model):
    title = models.CharField(max_length=255)
    real_type = models.ForeignKey(ContentType, editable=False)
    created = models.DateTimeField(auto_now_add=True, editable=False, verbose_name='creation date')

    def save(self):
        if not self.id:
            self.real_type = self._get_real_type()
            self.created = datetime.today()
        self.modified = datetime.today()
        super(ListType, self).save()

    def _get_real_type(self):
        return ContentType.objects.get_for_model(type(self))

class Note(Entry):
    text = models.TextField()

class File(Entry):
    file_field = models.FileField('File', upload_to='files')
    description = models.TextField(blank=True)

This means you can do the following in your view:

Entry.objects.all().order_by('created')
#get the corresponding child
note = Note.objects.get(entry_ptr_id=parent.pk)
print note.text

See my other post for detailed information regarding inheritance.

In order to save the "real type" of your entry in the model I have added a real_type field. This approach has been suggested in this post.

Having saved the real_type content type in your model allows you to query for all the entries and their details, without knowing upfront whether you are dealing with Notes or Files.

 entries = Entry.objects.all().order_by('created')
 for entry in entries:
     #get the correct child model
     childModel = entry.real_type.model_class()
     #get the child
     child = childModel.objects.get(pk=entry.pk)

     if entry.real_type.model == "Note":
         #it s a note object
         print entry.text

Note from the docs: model_class() returns the Python model class for this type of content.

Community
  • 1
  • 1
Thomas Kremmel
  • 14,575
  • 26
  • 108
  • 177
  • Thanks for answering! But the code in my initial post is simplified. Sorry, i shoud have mentiond that. It's updated now. I use inheritance. But the problem is about the corresponding view and the template for these model structure. – Jonas Sep 26 '12 at 17:46
  • Your updated example looks to me as a different question :D No Note, no File, but now we have Tags.. hurray. – Thomas Kremmel Sep 26 '12 at 18:01
  • Yes. Thats because the structure of the models is working fine. The question is about how to set up the view and template part. The Note became a Memo, Tags are not relevant, the only thing that matters is the generic foreign key. The File and Memo do not know anything about their existance in an GenericForeignKey field. – Jonas Sep 26 '12 at 20:00