django foreignkey通过related_name

时间:2015-07-18 20:52:02

标签: django

从我所阅读的内容和这里的一些帖子(以及阅读他们的解决方案)我认为我已经实现了这一点。看来我还没有完全掌握外键和related_name的东西。以下是我的模型以及我想要访问链接类的内容。

class Menu(models.Model):
    menu_name = models.CharField(max_length=25, verbose_name="Menu Name")
    urlconf_name = models.CharField(max_length=25, verbose_name="URLConf Name")
    menu_position = models.IntegerField(verbose_name="Menu Position", unique=True)
    has_sub_menu = models.BooleanField(default=False, verbose_name="Sub Menu Linked")
    active = models.BooleanField(default=True, verbose_name="Menu Active")

    def __str__(self):
        return self.menu_name

class Sub_Menu(models.Model):
    class Meta:
        ordering = ['menu_position']
    sub_name = models.CharField(max_length=25, verbose_name="Sub Menu Name")
    urlconf_name = models.CharField(max_length=25, verbose_name="Sub Menu URLConf Name")
    menu_position = models.IntegerField(verbose_name="Sub Menu Position")
    menu_id = models.ForeignKey(Menu, related_name="submenu")
    active = models.BooleanField(default=True, verbose_name="Menu Active")

    def __str__(self):
        return self.sub_name

现在我想我可以使用something.submenu(即related_name)简单地访问链接数据,但它似乎无法正常工作。通过上面的代码,我试过了;

the_menu = Menu.objects.filter(urlconf_name=split_path[0])
built_breadcrumb.append([the_menu[0].menu_name, split_path[0]])
if the_menu[0].has_sub_menu:
    built_breadcrumb.append([the_menu[0].submenu.sub_name, the_menu[0].submenu.urlconf_name])

那不起作用。我刚刚意识到它将返回一个项目列表,但我仍然在努力找出如何访问它们。我也试过了;

the_menu[0].submenu.filter(urlconf_name='home/contactus')

这也是错误。

我正在绞尽脑汁所以我有人可以解释如何通过相关名称进行访问我真的很感激。我已经完成了这项工作(通过这里的某人帮助),但由于某种原因,它似乎并没有在这种情况下发挥作用。

非常感谢。

韦恩

1 个答案:

答案 0 :(得分:3)

主要问题

事实(从您的模型中读取):

  • 菜单可以与许多子菜单相关。
  • 子菜单可以与一个菜单相关

考虑到这一点,请再次阅读以下这一行:

built_breadcrumb.append([the_menu[0].submenu.sub_name, the_menu[0].submenu.urlconf_name])

这部分具体:

the_menu[0].submenu.sub_name

submenu是一个查询集,因此您需要处理它。换句话说:“获取WHICH子菜单的sub_name?”

其他问题

.filter返回一个查询集,.get返回一个对象,所以你可能想要这样的东西:

the_menu = Menu.objects.get(urlconf_name=split_path[0])

现在the_menu包含对一个菜单的引用,因此您可以在后续行中删除后面的[0]

次要问题(不是真的,但是像地狱一样误导)

请勿在此处调用您的字段menu_id

class Sub_Menu(models.Model):
    ....
    menu_id = models.ForeignKey(Menu, ...)

原因如下:

the_menu = submenu.menu_id 
# the_menu is now an instance of a Menu class, not an ID

the_menu_id = submenu.menu_id.id  # the_menu_id is now an ID (integer)
# or:
the_menu_id = submenu.menu_id_id  # the_menu_id is now an ID (integer)

我建议用一种不那么混乱的方式命名你的字段:

class Sub_Menu(models.Model):
    ....
    menu = models.ForeignKey(Menu, ...)

现在与上面的例子相同:

the_menu = submenu.menu 
# the_menu is now an instance of a Menu class, not an ID

the_menu_id = submenu.menu.id  # the_menu_id is now an ID (integer)
# or:
the_menu_id = submenu.menu_id  # the_menu_id is now an ID (integer)

要求.menu并获得Menu是有道理的 要求.menu_id并获得Menu没有多大意义。

问题是Django在内部创建了一个字段来保存外键的ID,并通过在字段名称中添加“_id”来命名它,因此在第一个示例中为menu_id_id而在menu_id中第二个例子。

附加说明(假设更新的字段名称):

the_menu.menu.id会引发SubMenu.DoesNotExist,而the_menu.menu_id只会返回None。如果直接使用menu_id,那就要注意了。

同样,这是假设更改的字段名称。如果不是这样,只需添加_id

好的,让我们重新修改......

class Menu(models.Model):
    ...

class Sub_Menu(models.Model):
    sub_name = models.CharField(max_length=25, verbose_name="Sub Menu Name")
    menu = models.ForeignKey(Menu, related_name="submenus")
    ...

请注意更改:

  1. menu,而不是menu_id
  2. submenus,而不是submenu
  3. 好的,现在名字与他们实际引用的名称相符。

    try:
        the_menu = Menu.objects.get(urlconf_name=split_path[0])
    except Menu.DoesNotExist:
        # what to do if menu does not exist?
        return # or raise or something...
    built_breadcrumb.append([the_menu.menu_name, split_path[0]])
    if the_menu.has_sub_menu:  # this line is now probably not needed, just remove it and go straight to the for loop (of course, un-indent the for loop)
        for submenu in the_menu.submenus.filter(active=True):  # I added .filter(active... But if wanna go over all the submenus regardless of 'active', then use .all() instead of .filter(...)
            built_breadcrumb.append([submenu.sub_name, submenu.urlconf_name])
    

    这可以让你走上正确的轨道。

    等等,你想找到一对一的关系吗?

    在写我的答案时,我想到你可能想要在Menu和SubMenu类之间建立一对一的关系。如果是这种情况,则ForeignKey是该任务的错误工具。相反,你应该看看OneToOneField。这是如何工作的:

    class Menu(models.Model):
        ...
    
    class Sub_Menu(models.Model):
        sub_name = models.CharField(max_length=25, verbose_name="Sub Menu Name")
        menu = models.OneToOneField(Menu, related_name="submenu")
        ...
    
    
    try:
        the_menu = Menu.objects.get(urlconf_name=split_path[0])
    except Menu.DoesNotExist:
        # what to do if menu does not exist?
        return # or raise or something...
    built_breadcrumb.append([the_menu.menu_name, split_path[0]])
    try:
        built_breadcrumb.append([the_menu.submenu.sub_name, the_menu.submenu.urlconf_name])
    except SubMenu.DoesNotExist:
        # what to do it there is no submenu
        pass  # probably nothing, just move on
    

    最后的注释

    在任何情况下,我认为您不需要has_sub_menu字段,因为您可以从其他字段获取该信息。对于1对1关系,上面显示了try-except块。对于1对多(即外键)关系,for循环根本不会做任何事情。或者,如果您真的需要,可以像这样使用.count

    if the_menu.submenus.count() > 0:
        ....
    

    虽然请记住,这是对数据库的额外打击,并且可能没有必要(因此应该不鼓励)。