在我的django应用中,我有简单的Category和Offer模型:
class Category(BaseModel):
title = models.CharField(_('Category title'), max_length=256)
available = models.BooleanField(_('Is available'), default=True)
slug = models.SlugField(max_length=256, null=True, blank=True, unique=True, verbose_name=_(Slug'))
requires_item_price = models.BooleanField(default=False, verbose_name=_('Requires item price to be provided'))
class Offer(BaseModel):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True, verbose_name=_('Category'))
item_price = models.DecimalField(max_digits=8, decimal_places=2, null=True, verbose_name=_('Item price'),
blank=True)
def clean(self):
if self.category.requires_item_price and not self.item_price:
raise ValidationError({'item_price': _('If category requires item price - you have to provide it')})
我的表单类:
class NewPaginatedOfferForm(forms.ModelForm):
class Meta:
model = Offer
fields = ('item_price',)
def __init__(self, category, *args, **kwargs):
super().__init__(*args, **kwargs)
self.category = category
self.helper = FormHelper()
self.helper.form_id = self.__class__.__name__.lower()
self.initial['category'] = category
self.helper.layout = Layout(
Field('item_price'),
Div(
Submit('submit', _(Save →'),
css_class="btn btn-lg bold btn-block btn-success", ),
)
)
和我的视图类,基于通用的CreateView类:
class NewOfferForCategoryView(CreateView):
model = Offer
category = None
template_name = 'web/new_offer_for_category.html'
def get_form_class(self):
print('get_form_class')
if self.category.requires_item_price:
return NewPaginatedOfferForm
def get_form_kwargs(self):
print('get_form_kwargs')
kwargs = super().get_form_kwargs()
kwargs['category'] = self.category
print('kwargs:', kwargs)
return kwargs
def dispatch(self, request, *args, **kwargs):
print('dispatch, ', request.method)
try:
self.category = Category.objects.get(slug=self.kwargs.get('cat_slug'), available=True)
except:
print('wrong category')
return redirect(reverse('web:new_offer'))
print('self category is', self.category)
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
print('form_valid')
form.instance.category = self.category
return super().form_valid(form)
def form_invalid(self, form):
print('form_invalid')
form.instance.category = self.category
return super().form_invalid(form)
def get_context_data(self, **kwargs):
print('get_context_data')
ctx = super().get_context_data(**kwargs)
ctx['categories'] = Category.objects.filter(available=True)
ctx['category'] = self.category
return ctx
GET请求运行正常。问题是当我尝试提交此类表格时。 我总是遇到错误:
AttributeError at /new-offer/my-category-slug
'NoneType' object has no attribute 'requires_item_price'
Request Method: POST
Request URL: http://127.0.0.1:8000/new-offer/my-category-slug
Django Version: 2.2.6
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute 'requires_item_price'
Exception Location: /Users/User/project/core/models.py in clean, line 272
在Offer模型的清洁方法中,看起来类别属性为None-但我不确定为什么以及如何将其传递到那里。我想使用模型清理方法,因为当在其中创建新对象时,它也涵盖了django的管理面板验证。 有什么想法吗?
答案 0 :(得分:0)
您的模型允许空的category
(null=True
),因此您的clean()
方法应该不假定存在类别。添加if self.category and self.category.requires_item_price...
。
在您看来,您是在表单验证之后(即在调用模型的clean()方法之后)分配类别。相反,应该通过执行__init__()
在表单的初始化程序中分配它(因为您已经将其传递给self.instance.category = category
)。
您可以删除表单中的self.initial['category'] = category
,因为表单没有字段category
,所以该操作无济于事。您也可以删除self.category = category
,因为您没有在任何地方使用它。然后,您可以在视图中删除form.instance.category = category
。
在您的dispatch()
方法中,您还具有一个包含全部内容的except
子句。永远不要那样做,只捕获要捕获的异常,而不仅仅是任何异常。根据您的情况,将except:
替换为except Category.DoesNotExist:
。