1

我有一个问题,是否可以使用通用 UpdateView 类来编辑多对多关系的“双方”。我在 models.py 中定义了以下类:

class SomeCategory(models.Model):
    code = models.CharField(max_length=5)
    name = models.CharField(max_length=40)    


class SomeClass(models.Model):
    code = models.CharField(max_length=3, unique=True)
    name = models.CharField(max_length=30, unique=False)
    age = models.IntegerField(null=False)
    allowed_categories = models.ManyToManyField(SomeCategory)

这些都是字典类型的表,用于存储我的应用程序的配置数据集。为了允许编辑字典,我使用简单的 UpdateViews:

class SomeClassUpdate(UpdateView):
    model = SomeClass
    template_name = 'admin/edit_class.html'
    fields = ['code', 'name', 'age', 'allowed_categories']
    ordering = ['code']

这很好用,我得到了一个不错的多选,一切都很完美。但是,我希望能够从 SomeCategory 表的一侧编辑关系,因此我可以选择哪些 SomeClass 元素链接到某个 SomeCategory:

class SomeCategoryUpdate(UpdateView):
    model = SomeCategory
    template_name = 'admin/edit_category.html'
    fields = ['code', 'name',  ??????? ]
    ordering = ['code']

我尝试将related_name属性添加到 SomeCategory 模型,但这没有用。

有什么想法可以在不使用自定义 ModelForm 的情况下完成吗?

关键库版本:

Django==1.11.8
psycopg2==2.7.4

PS:这是我在 stackoverflow 上提出的第一个问题,所以如果我的帖子缺少任何强制性元素,请告诉我。

4

1 回答 1

3

您的问题在 models.py 文件中。你有两个课程,但只有一个课程提到了另一个课程。你会认为这应该足够了,因为你ManyToManyField毕竟在使用并假设它会自动创建双向的每个连接......不幸的是,这不是真的。在数据库级别,它确实创建了一个单独的中间表,其中引用了两个原始表中的对象,但这并不意味着它们都将在 Django Admin 或类似中自动可见。

如果您尝试简单地someclass = models.ManyToManyField(SomeClass)SomeCategory类中创建另一个会失败。Django 将尝试创建另一个单独的中间表,通过该中间表建立两个主表之间的连接。但是因为中间表的名称取决于您定义ManyToManyField连接的位置,所以第二个表将使用不同的名称创建,并且所有内容都会在逻辑上崩溃(两个表具有两种不同的默认方式来拥有 ManyToMany 连接是没有意义的)。

解决方案是添加一个ManyToManyField连接,SomeCategory同时还引用最初在SomeClass类中创建的中介/直通表。

关于 Django/python/命名/编程约定的几点说明:

  • 使用您所引用的表的名称作为包含有关该连接的信息的字段的名称。这意味着应该命名SomeClass带有链接的 's 字段而不是.SomeCategorysomecategoryallowed_categories
  • 如果连接是一对多 - 使用单数形式;如果连接是多对多的 - 使用复数。这意味着在这种情况下我们应该使用复数并使用somecategories而不是somecategory
  • Django 可以自动将名称复数,但它做得很糟糕——它只是s在末尾添加字母。Mouse-> Mouses, Category-> Categorys. 在这种情况下,您必须通过verbose_name_plural在特殊Meta类中定义来帮助它。
  • '仅当该类先前已在代码中定义时,才使用对其他类的引用而没有额外的 s。在两个类相互引用的情况下,只有一种方式是正确的。解决方案是将引用类的名称放在引号中,例如'SomeCategory'而不是SomeCategory. 这种参考,称为lazy relationship, 在解决两个应用程序之间的循环导入依赖关系时很有用。而且由于默认情况下最好保持样式相同并避免不必要的脑力浪费“我将根据课程的组织顺序决定是否使用引号;我将不得不重做这个引号的事情每次我决定移动一些代码片段时”我建议您每次都使用引号。就像学习驾驶汽车一样——最好学会始终使用转向信号灯,而不是先环顾四周,然后单独决定是否有人会从该信息中受益。
  • “字符串化”(延迟加载)模型/类/表名称很容易 - 只需添加's 即可。您会认为对“通过”表引用进行字符串化会以同样简单的方式工作。你会错的——它会给你ValueError: Invalid model reference. String model references must be of the form 'app_label.ModelName'.错误。为了引用字符串化的“通过”表,您需要: (a) 在'周围添加 s;(b) 将所有点 ( .) 替换为下划线 ( _);(c) 删除对through!.. 的引用,所以SomeClass.somecategories.through变成'SomeClass_somecategories'.

因此解决方案是这样的:

class SomeCategory(models.Model):
    code = models.CharField(max_length=5)
    name = models.CharField(max_length=40)
    someclasses = models.ManyToManyField('SomeClass', through='SomeClass_somecategories', blank=True)

    class Meta:
        verbose_name_plural = 'SomeCategories'


class SomeClass(models.Model):
    code = models.CharField(max_length=3, unique=True)
    name = models.CharField(max_length=30, unique=False)
    age = models.IntegerField(null=False)
    somecategories = models.ManyToManyField('SomeCategory')

在此之后,应该对你的UpdateView类进行什么样的最终更改是显而易见的。

于 2019-08-15T15:43:30.467 回答