可在字段集中使用的有序 ManyToManyField
我一直在研究一个有序的 ManyToManyField 小部件,并且它的前端方面工作得很好:
< img src="https://i.sstatic.net/APMce.png" alt="替代文本">
不幸的是,我在让后端工作时遇到了很多麻烦。连接后端的明显方法是使用 through
表将模型与关系两侧的 ForeignKey
联系起来,并覆盖 save 方法。这会很好用,但由于内容的特殊性,绝对要求将此小部件放置在字段集中(使用 ModelAdmin fieldsets
属性),即 显然不可能。
我没主意了。有什么建议吗?
谢谢!
I've been working through an ordered ManyToManyField widget, and have the front-end aspect of it working nicely:
Unfortunately, I'm having a great deal of trouble getting the backend working. The obvious way to hook up the backend is to use a through
table keyed off a model with ForeignKey
s to both sides of the relationship and overwrite the save method. This would work great, except that due to idiosyncrasies of the content, it is an absolute requirement that this widget be placed in a fieldset (using the ModelAdmin fieldsets
property), which is apparently not possible.
I'm out of ideas. Any suggestions?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
关于如何设置模型,您是对的,带有“订单”列的贯穿表是表示它的理想方式。你也是对的,Django 不会让你在字段集中引用该关系。解决此问题的技巧是记住,您在
ModelAdmin
的“fieldsets”或“fields”中指定的字段名称实际上并不引用Model
的字段>,但是到ModelForm
的字段,我们可以根据自己的喜好自由地覆盖它。对于many2many字段,这会变得很棘手,但请耐心等待:假设您试图代表比赛和参加其中竞争的竞争对手,在比赛和竞争对手之间使用有序的many2many,其中顺序代表竞争对手在该比赛中的排名。您的
models.py
将如下所示:希望这与您正在处理的内容类似。现在,对于管理员来说。我已经编写了一个示例
admin.py
,其中包含大量注释来解释发生的情况,但这里有一个摘要可以帮助您:由于我没有您所订购的 m2m 小部件的代码编写时,我使用了一个占位符虚拟小部件,它只是继承自
TextInput
。输入包含参赛者 ID 的逗号分隔列表(不带空格),它们在字符串中出现的顺序决定了 ContestResults 模型中“rank”列的值。发生的情况是,我们用自己的重写 Contest 的默认 ModelForm,然后在其中定义一个“结果”字段(我们不能将该字段称为“参赛者”,因为会有一个名称)与模型中的 m2m 字段冲突)。然后,我们重写
__init__()
,该函数在后台显示表单时调用,因此我们可以获取可能已为竞赛定义的任何 ContestResults,并使用它们来填充小部件。我们还重写save()
,以便我们可以依次从小部件获取数据并创建所需的 ContestResults。请注意,为了简单起见,此示例省略了诸如验证小部件中的数据之类的内容,因此如果您尝试在文本输入中输入任何意外的内容,事情就会中断。此外,创建 ContestResults 的代码非常简单,并且可以大大改进。
我还应该补充一点,我实际上已经运行了这段代码并验证了它的工作原理。
如果您想知道,我自己在我一直在从事的一个项目中遇到了这个问题,所以大部分代码都来自该项目。我希望你觉得它有用。
In regard to how to set up the models, you're right in that a through table with an "order" column is the ideal way to represent it. You're also right in that Django will not let you refer to that relationship in a fieldset. The trick to cracking this problem is to remember that the field names you specify in the "fieldsets" or "fields" of a
ModelAdmin
do not actually refer to the fields of theModel
, but to the fields of theModelForm
, which we are free to override to our heart's delight. With many2many fields, this gets tricky, but bear with me:Let's say you're trying to represent contests and competitors that compete in them, with an ordered many2many between contests and competitors where the order represents the competitors' ranking in that contest. Your
models.py
would then look like this:Hopefully, this is similar to what you're dealing with. Now, for the admin. I've written an example
admin.py
with plenty of comments to explain what's happening, but here's a summary to help you along:Since I don't have the code to the ordered m2m widget you've written, I've used a placeholder dummy widget that simply inherits from
TextInput
. The input holds a comma-separated list (without spaces) of contestant IDs, and the order of their appearance in the string determines the value of their "rank" column in theContestResults
model.What happens is that we override the default
ModelForm
for Contest with our own, and then define a "results" field inside it (we can't call the field "contestants", since there would be a name conflict with the m2m field in the model). We then override__init__()
, which is called when the form is displayed in the admin, so we can fetch any ContestResults that may have already been defined for the Contest, and use them to populate the widget. We also overridesave()
, so that we can in turn get the data from the widget and create the needed ContestResults.Note that for the sake of simplicity this example omits things like validation of the data from the widget, so things will break if you try to type in anything unexpected in the text input. Also, the code for creating the ContestResults is quite simplistic, and could be greatly improved upon.
I should also add that I've actually ran this code and verified that it works.
In case you're wondering, I had ran into this problem myself on a project I've been working on, so most of this code comes from that project. I hope you find it useful.