Django-使用通用UpdateView编辑多对多关系的两端 [英] Django - edit both sides of a many-to-many relation with generic UpdateView
问题描述
我有一个问题,是否可以使用通用的UpdateView类来编辑多对多关系的双方。
我在models.py中定义了以下类:
I have a question whether or not it is possible to use the generic UpdateView class to edit "both sides" of a many-to-many relationship. I have the following classes defined in 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:
These are both dictionary type tables that store sets of configuration data for my application. To allow editing the dictionaries I use simple UpdateViews:
class SomeClassUpdate(UpdateView):
model = SomeClass
template_name = 'admin/edit_class.html'
fields = ['code', 'name', 'age', 'allowed_categories']
ordering = ['code']
这很好,我得到了不错的选择,一切都很完美。但是,我希望可以从SomeCategory表的一侧编辑关系,因此我可以选择将SomeClass元素链接到某个SomeCategory:
This works fine, I get a nice multi-select and everything is perfect. However, I would like to have the possibility to edit the relationship from the side of the SomeCategory table, so I can choose which SomeClass elements are linked to a certain SomeCategory:
class SomeCategoryUpdate(UpdateView):
model = SomeCategory
template_name = 'admin/edit_category.html'
fields = ['code', 'name', ??????? ]
ordering = ['code']
我尝试添加 related_name
属性添加到SomeCategory模型,但这无效。
I have tried adding the related_name
attribute to the SomeCategory model, but that did not work.
有什么想法可以在不使用自定义ModelForm的情况下完成?
Any ideas if this can be done without using a custom ModelForm?
密钥库版本:
Django==1.11.8
psycopg2==2.7.4
PS:这是我对stackoverflow的第一个问题,所以请让我知道我的帖子是否缺少任何必填元素。
PS: this is my very first question asked on stackoverflow, so please let me know if my post is missing any mandatory elements.
推荐答案
您的问题在models.py文件中。您有两个班级,但其中只有一个班级提到另一班。您可能认为这应该足够了,因为您毕竟使用的是 ManyToManyField
并假定它将自动创建双向的每个连接...不幸的是,这不是事实。在数据库级别,确实确实创建了一个单独的中间表,其中引用了两个原始表中的对象,但这并不意味着它们都将在Django Admin或类似版本中自动显示。
Your issue is in the models.py file. You have two classes, but only one of them mentions the other one. You would think that this should be enough since you are using ManyToManyField
after all and assume that it would automatically create every connection leading both ways... Unfortunately this is not true. On the database level it does indeed create a separate intermediary table with references to objects in both original tables, but that doesn't mean that both of them will be automatically visible in Django Admin or similar.
如果您尝试简单地在 SomeCategory
someclass = models.ManyToManyField(SomeClass) >将会失败的类。 Django会尝试创建另一个独立的中介表,通过建立两个主表之间的连接。但是由于中间表的名称取决于定义 ManyToManyField
连接的位置,因此第二个表将使用不同的名称创建,并且所有内容在逻辑上都将折叠(两个表具有两种具有多对多连接的默认方法没有任何意义)。
If you would attempt to simply create another someclass = models.ManyToManyField(SomeClass)
in the SomeCategory
class that would fail. Django would try to create another separate intermediary table through which the connection between two main tables is established. But because the name of the intermediary table depends on where you define the ManyToManyField
connection, the second table would be created with a different name and everything would just logically collapse (two tables having two separate default ways to have a ManyToMany connection makes no sense).
解决方案是将 ManyToManyField
连接添加到 SomeCategory
还引用了最初在 SomeClass
类中创建的中介/穿透表。
The solution is to add a ManyToManyField
connection to SomeCategory
while also referencing that intermediary/through table that was originally created in the SomeClass
class.
一些注意事项关于Django / python / naming / programming约定:
A couple of notes about Django/python/naming/programming conventions:
- 使用您要引用的表的名称作为该字段的名称包含有关该连接的信息。这意味着
SomeClass
的具有SomeCategory
链接的字段应命名为somecategory
而不是allowed_categories
。 - 如果连接是一对多-使用单数形式;如果连接是多对多-使用复数。意味着在这种情况下,我们应该使用复数并使用
somecategories
而不是somecategory
。 - Django可以自动将名称复数,但是这样做很糟糕-它只是在末尾添加
s
字母。鼠标
->鼠标
,类别
->类别
。在这种情况下,您必须通过在特殊的Meta
类中定义verbose_name_plural
来提供帮助。 - 仅在代码中先前已经定义了该类的情况下,才使用对其他类的引用而无需额外的
'
。在两个类相互引用的情况下,这仅是一种方式。解决方案是将引用类的名称放在引号中,例如SomeCategory
,而不是SomeCategory
。这种引用称为惰性关系
,在解决两个应用程序之间的循环导入依赖关系时很有用。而且由于默认情况下最好保持样式相同,避免不必要的浪费精力,我将根据课程的组织顺序来决定是否使用引号;我将不得不重做该引号每次我决定移动一些代码段时,我建议您每次都只使用引号。就像在学习驾驶汽车时一样,最好学会始终使用转向信号灯,而不是先环顾四周并做出是否有人会从该信息中受益的单独决定。 - 字符串化(延迟加载)模型/类/表的名称很容易-只需添加
'
即可。您可能会认为,对通过表引用进行字符串化将以相同的简便方式工作。而且您会错的-它会为您提供ValueError:无效的模型引用。字符串模型引用的格式必须为 app_label.ModelName。
错误。为了引用字符串化的直通表格,您需要:(a)在周围添加; (b)将所有点(
。
)替换为下划线(_
); (c)删除对through
!的引用。.因此,SomeClass.somecategories.through
变为'SomeClass_somecategories'
。
- Use the name of the table you are referencing to, as the name of the field that is containing the info about that connection. Meaning that
SomeClass
's field with a link toSomeCategory
should be namedsomecategory
instead ofallowed_categories
. - If the connection is one-to-many - use singular form; if the connection is many-to-many - use plural. Meaning that in this case we should use plural and use
somecategories
instead ofsomecategory
. - Django can automatically pluralize names, but it does it badly - it simply adds
s
letter to the end.Mouse
->Mouses
,Category
->Categorys
. In those kind of cases you have to help it by defining theverbose_name_plural
in the specialMeta
class. - Using references to other classes without extra
'
s works only if the the class was already defined previously in the code. In the case of two classes referring to each other that is true only one way. The solution is to put the name of the referred class in the quotation marks like'SomeCategory'
instead ofSomeCategory
. This sort of reference, called alazy relationship
, can be useful when resolving circular import dependencies between two applications. And since by default it's better to keep the style the same and to avoid unnecessary brain energy wasting of "I will decide whether or not to use quotation marks depending on the order the classes have been organized; I will have to redo this quotation marks thingie every time I decide to move some code pieces around" I recommend that you simply use quotation marks every time. Just like when learning to drive a car - it's better to learn to always use turn signals instead of first looking around and making a separate decision of whether someone would benefit from that information. - "Stringifying" (lazy loading) model/class/table name is easy - just add
'
s around. You would think that stringifying the "through" table reference would work the same easy way. And you would be wrong - it will give you theValueError: Invalid model reference. String model references must be of the form 'app_label.ModelName'.
error. In order to reference the stringified "through" table you need to: (a) add'
s around; (b) replace all dots (.
) with underscores (_
); (c) delete the reference tothrough
!.. SoSomeClass.somecategories.through
becomes'SomeClass_somecategories'
.
因此解决方案是这样的:
Therefore the solution is this:
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
类进行什么样的最终更改。
After this it should be obvious what kind of final changes to make to your UpdateView
classes.
这篇关于Django-使用通用UpdateView编辑多对多关系的两端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!