A Model to store links to other models

17 views
Skip to first unread message

Micah Carrick

unread,
Nov 19, 2010, 8:51:52 PM11/19/10
to django...@googlegroups.com
I'm having trouble coming up with a pretty solution to a seemingly simple task. I'm relatively new to Django.

I want to allow the end user to control various lists of links on the site used for navigation. The admin should allow the creation of "link groups" which have a collection of "links". These links would reference a pre-determined list of models. Let's say, for example, that there is a "link group" created for the footer links of a website. In the admin section, a user could add a link to a specific blog (from the Blog model), another link to the about us page (flatpages), etc.

In other words, I'm trying to associate individual records from a number of tables together as a group of objects having a URL. I'm trying to find a nice, abstract solution. Obviously I don't want actual URLs in the database. This is something I would use frequently so I want to see if I can find or write an app to do this--if I can come up with an elegant solution.

This would be nice, but, I can't imagine how it could be possible:


class LinkGroup(models.Model):
    site = models.ForeignKey(Site)
    name = models.CharField()

class Links(models.Model):
    link_group = ForeignKey(LinkGroup)
    model_type = ???
    model_id = ForeignKey(<to the PK of the above the model_type>) # no can do!
    sort_order = PositiveIntegerField(default=100)


This is an idea, however, I don't like having to reference the import in the DB. It's just begging for problems.


class LinkModel(models.Model):
    name = models.CharField() # such as "Flat Page"
    model = models.CharField() # such as "myapp.models.FlatPage"

class LinkGroup(models.Model):
    site = models.ForeignKey(Site)
    name = models.CharField() # such as "Navigation Links"

class Link(models.Model):
    text = CharField() # such as "About Us"
    link_group = ForeignKey(LinkGroup)
    model = ForeignKey(LinkModel)
    model_id = PositiveIntegerField() # such as the PK for the myapp.models.FlatPage model
    sort_order = PositiveIntegerField(default=100)


Any suggestions?


Steve Holden

unread,
Nov 20, 2010, 1:07:02 AM11/20/10
to django...@googlegroups.com
What's wrong with the first approach? I use a similar technique to put
pages in sections at http://holdenweb.com/ - here are the relevant models:

class Section(models.Model):
secid = models.IntegerField(primary_key=True) # This field type is
corrected
sectitle = models.CharField(max_length=50)
secpath = models.CharField(max_length=50)
secbgcolor = models.CharField(max_length=50)
secstcolor = models.CharField(max_length=50)
secpos = models.CharField(max_length=1)
secsequence = models.IntegerField() # This field type is corrected
sechandler = models.CharField(max_length=50)
sechomeslot = models.IntegerField() # This field type is corrected
sechometitle = models.CharField(max_length=50)
secnews = models.CharField(max_length=20, null=True)
class Meta:
db_table = 'section'
unique_together = (('secpath', ), ('sectitle', ))
class Admin:
list_display = ('sectitle', )
def __str__(self):
return self.sectitle

class Page(models.Model):
pagpath = models.CharField(primary_key=True, max_length=50)
pagdoctitle = models.CharField(max_length=250)
pagtitle = models.CharField(max_length=200, blank=True, null=True)
pagsecid = models.ForeignKey(Section, blank=True, null=True,
db_column='pagsecid') # This field
type is corrected
pagsequence = models.IntegerField() # This field type is corrected
pagtplname = models.CharField(max_length=50)
paggenerated = models.SmallIntegerField()
paglinkpath = models.CharField(max_length=50, blank=True, null=True)
pagcontent = models.TextField()
pagnavbarstuff = models.TextField(null=True, blank=True)
pagctype = models.CharField(max_length=1, default="R") # This field
type is corrected
pagid = models.IntegerField() # Note this should eventually become
the PK
pagnews = models.CharField(max_length=20, blank=True, null=True)
class Meta:
db_table = 'page'
def __str__(self):
return self.pagpath

What is it that the first model didn't do for you?

regards
Steve
--
DjangoCon US 2010 September 7-9 http://djangocon.us/

Mark (Nosrednakram)

unread,
Nov 20, 2010, 8:07:52 AM11/20/10
to Django users
Hello Micah,

I'm not sure I understand what you're attempting to do but if so I'd
look at using a foreign key to ContentType, and storing the pk for
associating objects rather than a CharField. The key is using
ContentType.model_class() to re-create the class. This allows you to
store any object using two fields and re-generate it easly in two
calls.

Hope This Helps,
Mark

Łukasz Rekucki

unread,
Nov 20, 2010, 8:09:32 AM11/20/10
to django...@googlegroups.com

You should checkout generic foreign keys[1]. It's a standard way of
building models that need to reference rows in more then one table. So
your models would be something like this:

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

class LinkGroup(models.Model):
site = models.ForeignKey(Site)
name = models.CharField() # such as "Navigation Links"

class Link(models.Model):
link_group = ForeignKey(LinkGroup)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
linked_object = generic.GenericForeignKey('content_type', 'object_id')


[1]: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#generic-relations

--
Łukasz Rekucki

Micah Carrick

unread,
Nov 20, 2010, 8:17:39 AM11/20/10
to django...@googlegroups.com
I had not seen the generic relationships and ContentType. I'm going to play with that a bit.

Thanks everybody!

2010/11/20 Łukasz Rekucki <lrek...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.



Micah Carrick

unread,
Nov 22, 2010, 10:11:33 AM11/22/10
to django...@googlegroups.com
Hey guys,

I could use some advice on the next steps for the administration. I am passing the link groups to the template via context so that the template can iterate over each group creating a unordered list of links for each group. Some links are blog posts, some are flat pages, etc. Works great. However, there are two parts of the admin I could use some guidance on. Links to docs, blogs, or example code would be great.

First, in the drop down list for the ContentType, I need to exclude any object that do not define get_absolute_url. In other words, only models that can be linked to should be allowed.

Second, and this may be more difficult, the object_id field requires the admin user to know the PK of the object their linking to, which they most certainly will not. However, I don't know how that could be determined without first knowing which content_type they select-- AJAX is the only thing I can think of. Thoughts?


class LinkGroup(models.Model):
    site = models.ForeignKey(Site)
    # template_var is the name of the list of links passed in context to template
    template_var = models.CharField(max_length=25, unique=True, db_index=True)
    heading = models.CharField(max_length=100, blank=True, null=True)
   
    def __unicode__(self):
        return self.template_key
       
    class Meta():
        db_table = 'link_group'

class LinkObject(models.Model):
    text = models.CharField(max_length=50) # text for the link
    link_group = models.ForeignKey(LinkGroup)

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    sort_order = models.PositiveIntegerField(default=100)
   
    def __unicode__(self):
        return self.text
       
    class Meta():
        db_table = 'link_object'

class LinkGroupInline(admin.TabularInline):
    model = LinkObject
   
class LinkGroupAdmin(admin.ModelAdmin):
    inlines = [
        LinkGroupInline
    ]
   
admin.site.register(LinkGroup, LinkGroupAdmin)
--
Micah Carrick, Founder

    Green Tackle - Environmentally Friendly Fishing Tackle
    www.GreenTackle.com

     Email: mi...@greentackle.com
     Phone: 971.270.2206
     Toll Free: 877.580.9165
     Fax: 503.946.3106



Reply all
Reply to author
Forward
0 new messages