考虑以下模型和形式:
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
当你查看ToppingForm时,它使你可以选择浇头所用的披萨,而一切都太花哨了。
我的问题是:如何为披萨定义一个ModelForm,让我利用披萨和馅料之间的多对多关系,并让我选择披萨上的馅料?
我想你会在这里新添加ModelMultipleChoiceField到你PizzaForm,并手动链接,表单字段与模型领域,如Django会不会为你做自动。
ModelMultipleChoiceField
PizzaForm
以下代码段可能会有所帮助:
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() instance.topping_set.add(*self.cleaned_data['toppings']) self.save_m2m = save_m2m # Do we need to save all changes now? if commit: instance.save() self.save_m2m() return instance
这PizzaForm然后可以使用无处不在,甚至在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)
注意
该save()方法可能有点过于冗长,但是如果你不需要支持这种commit=False情况,则可以将其简化,如下所示:
save()
commit=False
def save(self): instance = forms.ModelForm.save(self) instance.topping_set.clear() instance.topping_set.add(*self.cleaned_data['toppings']) return instance