Django - 使用表单和视图中的中间表处理多对多的好方法是什么

发布于 2024-09-26 19:02:32 字数 1937 浏览 5 评论 0原文

假设我有以下模型 -

class Item(models.Model):
    name = models.CharField(max_length=150)
    value = models.DecimalField(max_digits=12,decimal_places=2)

class Organization(models.Model):
    name = models.CharField(max_length=150)
    items = models.ManyToManyField(Item, through='Customizable')

class Customizable(models.Model):
    organization = models.ForeignKey(Organization)
    item = models.ForeignKey (Item)
    value = models.DecimalField(max_digits=12,decimal_places=2)

通常,当项目被“分配”给组织时,它们将具有与相关中最初记录的相同值>Item 对象。但在某些情况下,分配给组织的项目可能具有被覆盖的(因此出现了中介模型)。由于覆盖原始值很少发生(但确实发生),我希望允许用户只需从 Item 实例列表中选择所需的 items 即可将它们分配给组织实例。批量分配完成后,用户可以选择覆盖各个值。

所以我有以下简单的 ModelForm -

class AssignItemsForm(forms.ModelForm):
    items = forms.ModelMultipleChoiceField(queryset=Item.objects.all(),required=False,widget=forms.CheckboxSelectMultiple)
    class Meta:
        model = Organization
        exclude = ('name',)

现在由于我有一个 through 模型,简单的 form.save() 将不起作用。我需要 (i) 保存与用户选择的项目相对应的Customized实例,并且 (ii) 确保持久化的可自定义实例具有从item获取的相应中获取的正确通过foreignkey关联的实例。

我试图在视图中处理它(但我的思想被阻止了)-

def assign_items(request, oid):
    organization = Organization.objects.get(id=oid)
    if request.method == 'POST':
        form = AssignItemsForm(data=request.POST, instance=organization)
        if form.is_valid():
            current_organization = form.save(commit=False)
            #
            #placeholder to save Customizable instances here
            #
            return HttpResponseRedirect(reverse('redirect-someplace-else'))
    else:
        form = AssignItemsForm(instance=organization,)
    return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request))

Suppose I have the following models -

class Item(models.Model):
    name = models.CharField(max_length=150)
    value = models.DecimalField(max_digits=12,decimal_places=2)

class Organization(models.Model):
    name = models.CharField(max_length=150)
    items = models.ManyToManyField(Item, through='Customizable')

class Customizable(models.Model):
    organization = models.ForeignKey(Organization)
    item = models.ForeignKey (Item)
    value = models.DecimalField(max_digits=12,decimal_places=2)

More often than not, when items are "assigned" to an organization, they will have the same value as originally recorded in the related Item object. But in certain cases, an item assigned to an organization may have an overridden value (hence the intermediary model). Since overriding the original value happens rarely (but it does happen) I want to allow the user to simply select desired items from a list of Item instances to assign them to an organization instance. The user will then have the option of overriding individual values later after bulk assignment is complete.

So I have the following simple ModelForm -

class AssignItemsForm(forms.ModelForm):
    items = forms.ModelMultipleChoiceField(queryset=Item.objects.all(),required=False,widget=forms.CheckboxSelectMultiple)
    class Meta:
        model = Organization
        exclude = ('name',)

Now since I have a through model, a simple form.save() won't work. I need to
(i) save Customizable instances corresponding to the items selected by the user and
(ii) make sure the persisted Customizable instances have the proper value taken from the corresponding value taken from the item instance related by foreignkey .

I am trying to handle it in a view (but my mind is blocked) -

def assign_items(request, oid):
    organization = Organization.objects.get(id=oid)
    if request.method == 'POST':
        form = AssignItemsForm(data=request.POST, instance=organization)
        if form.is_valid():
            current_organization = form.save(commit=False)
            #
            #placeholder to save Customizable instances here
            #
            return HttpResponseRedirect(reverse('redirect-someplace-else'))
    else:
        form = AssignItemsForm(instance=organization,)
    return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦巷 2024-10-03 19:02:32

您必须使用 save_m2m 方法:

def assign_items(request, oid):
    organization = Organization.objects.get(id=oid)
    if request.method == 'POST':
        form = AssignItemsForm(data=request.POST, instance=organization)
        if form.is_valid():
            current_organization = form.save(commit=False)

            current_organization.save()

            form.save_m2m()

            return HttpResponseRedirect(reverse('redirect-someplace-else'))
    else:
        form = AssignItemsForm(instance=organization,)
    return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request))

在此处查看更多信息:

http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method

You would have to use save_m2m method:

def assign_items(request, oid):
    organization = Organization.objects.get(id=oid)
    if request.method == 'POST':
        form = AssignItemsForm(data=request.POST, instance=organization)
        if form.is_valid():
            current_organization = form.save(commit=False)

            current_organization.save()

            form.save_m2m()

            return HttpResponseRedirect(reverse('redirect-someplace-else'))
    else:
        form = AssignItemsForm(instance=organization,)
    return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request))

Look here for more info:

http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method

烟─花易冷 2024-10-03 19:02:32

我会以不同的方式处理这个问题。您有一个 M2M 中介模型。因此,我认为 AssignItemsForm 应该由这个中介模型支持。因此,我将其更改如下:

# forms.py
class AssignItemsForm(forms.ModelForm):
    value = forms.DecimalField(max_digits=12, decimal_places=2, required = False)

    class Meta:
        model = Customizable

接下来,是允许用户选择不同值的问题。为此,我将模型的 value 字段设为可选 (required = False)。然后我检查用户是否提供了明确的值。如果不是,我假设将使用 Item 的默认值。为此,我重写了表单的 clean 方法:

    def clean(self):
        super(AssignItemsForm, self).clean()
        value, item = self.cleaned_data.get('value'), self.cleaned_data.get('item')
        if not value:
            value = item.value
        self.cleaned_data['value'] = value
        return self.cleaned_data

最后我在管理中测试了它。

# admin.py
from app.forms import AssignItemsForm

class CAdmin(admin.ModelAdmin):
    form = AssignItemsForm

admin.site.register(Item)
admin.site.register(Organization)
admin.site.register(Customizable, CAdmin)

这样您就可以继续使用 form.save() 从而避免在视图中进行自定义操作。您必须稍微更改一下视图,以确保自动选择组织来分配项目。

# views.py
def assign_items(request, oid):
    organization = Organization.objects.get(id=oid)
    if request.method == 'POST':
        form = AssignItemsForm(data=request.POST.copy())
        form.save()
    else:
        form = AssignItemsForm(initial = {'organization': organization})
    ...

I'd approach this in a different way. You have an intermediary model for your m2m. Hence I'd argue that AssignItemsForm should be backed by this intermediary model. Therefore I'd change it as follows:

# forms.py
class AssignItemsForm(forms.ModelForm):
    value = forms.DecimalField(max_digits=12, decimal_places=2, required = False)

    class Meta:
        model = Customizable

Next, the matter of allowing users to choose a different value. In order to do this I've made the value field of the model optional (required = False). I then check if the user has supplied an explicit value. If not I assume that the Item's default value is to be used. For this I am overriding the clean method of the form:

    def clean(self):
        super(AssignItemsForm, self).clean()
        value, item = self.cleaned_data.get('value'), self.cleaned_data.get('item')
        if not value:
            value = item.value
        self.cleaned_data['value'] = value
        return self.cleaned_data

And finally I tested this in admin.

# admin.py
from app.forms import AssignItemsForm

class CAdmin(admin.ModelAdmin):
    form = AssignItemsForm

admin.site.register(Item)
admin.site.register(Organization)
admin.site.register(Customizable, CAdmin)

This way you can continue to use form.save() thereby avoiding custom manipulation in the view. You'll have to change your view a bit to make sure that the organization is auto selected for assigning items.

# views.py
def assign_items(request, oid):
    organization = Organization.objects.get(id=oid)
    if request.method == 'POST':
        form = AssignItemsForm(data=request.POST.copy())
        form.save()
    else:
        form = AssignItemsForm(initial = {'organization': organization})
    ...
情魔剑神 2024-10-03 19:02:32

重写 ModelForm 的 save 方法。这样,如果您需要在多个地方使用该表单,则无需重复操作。

有关更多详细信息,请参阅此答案:

https://stackoverflow.com/a/40822731/2863603

Override the save method of the ModelForm. This way you won't have to repeat yourself if you need to use the form in multiple places.

See this answer for more details:

https://stackoverflow.com/a/40822731/2863603

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文