Django ModelForm for many to many many fields [英] Django ModelForm for Many-to-Many fields

查看:161
本文介绍了Django ModelForm for many to many many fields的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下型号和形式:

  class Pizza(models.Model):
name = models。 CharField(max_length = 50)

class Topping(models.Model):
name = models.CharField(max_length = 50)
ison = models.ManyToManyField(Pizza,blank =真的)

class ToppingForm(forms.ModelForm):
class Meta:
model = Topping

当您查看ToppingForm时,可以让您选择什么比萨饼,而且所有东西都只是花花公子。



我的问题是:我如何定义一个可以让Pizza利用Pizza和Topping之间的多对多关系的Pizza的ModelForm,让我选择Pizza上的Toppings?

解决方案

我想你可以在这里添加一个新的 ModelMultipleChoiceField 到你的 PizzaForm ,并手动将该表单字段与模型字段链接,因为Django将不会自动执行对于您。



以下代码段可能会有帮助:

  class PizzaForm(forms.ModelForm):
class Meta:
model = Pizza

#表示Pizza中的许多相关字段
toppings = forms.ModelMultipleChoiceField(queryset = Topping.objects.all())

#覆盖__init__这里允许我们为'toppings'字段提供初始
#数据
def __init __(self,* args,* * kwargs):
#只有在我们从一个实例
#构建表单(否则'toppings'列表应为空)
如果kwargs.get('instance'):
#如果不存在,我们得到'initial'关键字参数或初始化
#作为dict。
initial = kwargs.setdefault('initial',{})
#ModelMultipleChoiceField的窗口小部件需要
#所选数据的主键列表。
initial ['toppings'] = [t.pk for t in kwargs ['instance']。topping_set.all()]

forms.ModelForm .__ init __(self,* args, ** kwargs)

#覆盖保存允许我们处理'toppings'字段的值
def save(self,commit = True):
#获取unsave比萨实例
instance = forms.ModelForm.save(self,False)

#为表单准备一个'save_m2m'方法,
old_save_m2m = self.save_m2m
def save_m2m ():
old_save_m2m()
#这是我们实际链接比萨饼与浇头
instance.topping_set.clear()
在self.cleaned_data ['toppings' ]:
instance.topping_set.add(topping)
self.save_m2m = save_m2m

#我们现在需要保存所有更改吗?
如果提交:
instance.save()
self.save_m2m()

返回实例

这个 PizzaForm 可以随处使用,即使在管理员中:



pre $ #yourapp / admin.py
from django.contrib.admin import site,ModelAdmin
from yourapp.models import Pizza
from yourapp .forms import PizzaForm

class PizzaAdmin(ModelAdmin):
form = PizzaForm

site.register(Pizza,PizzaAdmin)



注意



save()方法可能有点太冗长了,但如果您不需要支持 commit = False 的情况,您可以简化它,那么它将是这样的:

$ b

  def save(self):
instance = forms.ModelForm.save(self)
instance。 topping_set.clear()
在self.cleaned_data ['toppings']中的顶部:
instance.topping_set.add(topping)


Consider the following models and form:

class Pizza(models.Model):
    name = models.CharField(max_length=50)

class Topping(models.Model):
    name = models.CharField(max_length=50)
    ison = models.ManyToManyField(Pizza, blank=True)

class ToppingForm(forms.ModelForm):
    class Meta:
        model = Topping

When you view the ToppingForm it lets you choose what pizzas the toppings go on and everything is just dandy.

My questions is: How do I define a ModelForm for Pizza that lets me take advantage of the Many-to-Many relationship between Pizza and Topping and lets me choose what Toppings go on the Pizza?

解决方案

I guess you would have here to add a new ModelMultipleChoiceField to your PizzaForm, and manually link that form field with the model field, as Django won't do that automatically for you.

The following snippet might be helpful :

class PizzaForm(forms.ModelForm):
    class Meta:
        model = Pizza

    # Representing the many to many related field in Pizza
    toppings = forms.ModelMultipleChoiceField(queryset=Topping.objects.all())

    # Overriding __init__ here allows us to provide initial
    # data for 'toppings' field
    def __init__(self, *args, **kwargs):
        # Only in case we build the form from an instance
        # (otherwise, 'toppings' list should be empty)
        if kwargs.get('instance'):
            # We get the 'initial' keyword argument or initialize it
            # as a dict if it didn't exist.                
            initial = kwargs.setdefault('initial', {})
            # The widget for a ModelMultipleChoiceField expects
            # a list of primary key for the selected data.
            initial['toppings'] = [t.pk for t in kwargs['instance'].topping_set.all()]

        forms.ModelForm.__init__(self, *args, **kwargs)

    # Overriding save allows us to process the value of 'toppings' field    
    def save(self, commit=True):
        # Get the unsave Pizza instance
        instance = forms.ModelForm.save(self, False)

        # Prepare a 'save_m2m' method for the form,
        old_save_m2m = self.save_m2m
        def save_m2m():
           old_save_m2m()
           # This is where we actually link the pizza with toppings
           instance.topping_set.clear()
           for topping in self.cleaned_data['toppings']:
               instance.topping_set.add(topping)
        self.save_m2m = save_m2m

        # Do we need to save all changes now?
        if commit:
            instance.save()
            self.save_m2m()

        return instance

This PizzaForm can then be used everywhere, even in the admin :

# yourapp/admin.py
from django.contrib.admin import site, ModelAdmin
from yourapp.models import Pizza
from yourapp.forms import PizzaForm

class PizzaAdmin(ModelAdmin):
  form = PizzaForm

site.register(Pizza, PizzaAdmin)

Note

The save() method might be a bit too verbose, but you can simplify it if you don't need to support the commit=False situation, it will then be like that :

def save(self):
  instance = forms.ModelForm.save(self)
  instance.topping_set.clear()
  for topping in self.cleaned_data['toppings']:
    instance.topping_set.add(topping)

这篇关于Django ModelForm for many to many many fields的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆