问题描述
考虑以下型号和形式:
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!