Django 按通用外键排序

1 投票
2 回答
2373 浏览
提问于 2025-04-16 05:44

嘿,这里有个简单的问题。我有两个模型,分别是帖子(Post)和标签(Term),我想给帖子和将来的其他模型添加标签和分类(也就是分类法)。我的帖子模型有以下几个字段:标题(title)、内容(content)、发布时间(published),而我的标签模型是这样定义的:

class Term(models.Model):
    taxonomy = models.CharField(max_length=255)
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=50)

接着,我有一个标签关系模型(TermRelation),它把标签和帖子以及其他模型关联起来,像这样:

class TermRelation(models.Model):
    term = models.ForeignKey(Term)
    object_id = models.PositiveIntegerField()
    content_type = models.ForeignKey(ContentType)
    content_object = generic.GenericForeignKey()

一切都按预期工作,但我有个问题。我想创建一个特定分类的帖子归档,并按照发布时间对帖子进行排序。这是我想做的:

ctype = ContentType.objects.get_for_model(Post)
relations = TermRelation.objects.filter(content_type__pk=ctype.id)

这样做是可以的,虽然我猜它是按关系的主键(PK)排序的。当我尝试以下操作时:

relations = TermRelation.objects.filter(content_type__pk=ctype.id).order_by('content_object__published')

我收到一个错误,提示在标签关系模型中没有内容对象(content_object)字段。我知道一定有办法解决这个问题。有什么想法吗?

谢谢 ~ K

2 个回答

1

据我所知,这在Django中是做不到的,因为order_by的参数会被转换成数据库中的列或表,而content_object在数据库中并不存在。这个原因也是你需要通过对象ID和内容类型来查找内容类型的原因...

2

是的,我之前说的关于简单的SQL查询和连接的确是对的。实际上有几个连接。以下是我用的代码(适用于django >= 1.2的raw()方法)

data = {
    'posts': Post._meta.db_table,
    'relations': TermRelation._meta.db_table,
    'terms': Term._meta.db_table,
    'tag_id': tag.id
}

posts = Post.objects.raw('SELECT %(posts)s.* FROM %(posts)s JOIN %(relations)s ON %(posts)s.id = %(relations)s.object_id JOIN %(terms)s ON %(relations)s.term_id = %(terms)s.id WHERE %(terms)s.id = %(tag_id)s ORDER BY %(posts)s.published DESC' % data)

其实并不难。也许我应该把它封装成一个TermRelation的方法,你觉得怎么样?

更新:我确实把它封装成了一个静态方法,并且让它可以处理不同的内容类型和排序方式。它仍然假设有一个叫做content_type_id的字段,如果有外键指向ContentType,这个字段会默认创建。以下是代码:

@staticmethod
def get_objects_by_term_id(model=None, taxonomy=None, term_id=None, order_by='NULL'):
    data = {
        'objects': model._meta.db_table,
        'content_type': ContentType.objects.get_for_model(model).id,
        'relations': TermRelation._meta.db_table,
        'terms': Term._meta.db_table,
        'term_id': term_id,
        'order_by': ' ORDER BY %s ' % order_by
    }

    return model.objects.raw('SELECT %(objects)s.* FROM %(objects)s JOIN %(relations)s ON %(objects)s.id = %(relations)s.object_id AND %(relations)s.content_type_id = %(content_type)s JOIN %(terms)s ON %(relations)s.term_id = %(terms)s.id WHERE %(terms)s.id = %(term_id)s %(order_by)s' % data)

撰写回答