我正在尝试使用crispy_forms创建一个表单来添加新的汽车广告。它有一个能够使用django内联formset添加汽车照片的字段集。它在form_valid的情况下效果很好 - 我的广告创建成功,但是如果是form_invalid(例如,错过了必填字段),我会在没有上传图像的情况下返回我的表单 - 清除图像字段集。如何保留formset数据? 这是我的代码:
models.py
class AdCar(CarEquipment, AdBase):
""" Model of Car advertisements """
car_brand = models.ForeignKey(CarBrand)
car_model = models.ForeignKey(CarModel)
launch_year = models.IntegerField(choices=LAUNCH_YEARS)
state = models.IntegerField(choices=AUTO_STATE, default=None)
...
@python_2_unicode_compatible
class ProductImage(models.Model):
product = models.ForeignKey(AdBase, related_name='images')
src = models.ImageField(upload_to="photos")
objects = ImageManager()
class Meta:
ordering = ['id']
def __str__(self):
return "{0}{1}".format(MEDIA_URL, self.src)
forms.py
class AddCarAdvertForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AddCarAdvertForm, self).__init__(*args, **kwargs)
self.fields['steer'].required = False
self.fields['electric_windows_lift'].required = False
try:
self.fields['car_model'] = forms.ModelChoiceField(
queryset=CarModel.objects.filter(brand=self.initial['car_brand']),
label = 'Model'
)
except KeyError:
pass
self.helper = FormHelper()
self.helper.form_tag = False
# self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-xs-4'
self.helper.field_class = 'col-xs-8'
self.helper.layout = Layout(
Div(
Div('seller_city', css_class='col-xs-10 text-center', css_id='city_div'),
Div(Div('car_brand', css_class='col-xs-6'), Div('car_model', css_class='col-xs-6',
css_id='car_model_div'))
...
class ImageForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.template = 'bootstrap/table_inline_formset.html'
self.helper.layout = Layout(
Field('src'),
)
super(ImageForm, self).__init__(*args, **kwargs)
class Meta:
model = ProductImage
fields = ('src',)
AdInlineFormSet = forms.models.inlineformset_factory(AdCar, ProductImage, form=ImageForm)
views.py
class AddCarAdvertView(CreateView):
template_name = 'ads/car_advert_form.html'
success_url = reverse_lazy('ad_success')
model = AdCar
form_class = AddCarAdvertForm
def get_initial(self):
initial = {}
if self.request.user.is_authenticated():
initial['seller_email'] = self.request.user.email
initial['seller_name'] = self.request.user.first_name
try:
initial['seller_city'] = self.request.session['city_id']
except KeyError:
pass
try:
initial['car_brand'] = self.request.session['ad_car_brand']
except KeyError:
pass
return initial
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
img_form = AdInlineFormSet()
return self.render_to_response(self.get_context_data(form=form, img_form=img_form))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
img_form = AdInlineFormSet(self.request.POST, self.request.FILES)
if (form.is_valid() and img_form.is_valid()):
return self.form_valid(form, img_form)
else :
return self.form_invalid(form, img_form)
def form_valid(self, form, img_form):
self.object = form.save()
img_form.instance = self.object
img_form.save()
def form_invalid(self, form, img_form):
return self.render_to_response(self.get_context_data(form=form, img_form=img_form))
模板
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% crispy form %}
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<div class="panel panel-default">
<div class="panel-heading" style="background-color:#D93832">
<h2 class="panel-title" style="font-size:1.2em">Photo block</h2>
</div>
<div class="panel-body">
{% crispy img_form img_form.form.helper %}
</div>
</div>
</div>
</div>
<div class="text-center">
<input id="submit-id-submit" class="btn btn-primary btn-lg" name="submit" type="submit" value="Save">
</div>
</form>