В моем приложении django у меня есть простые модели категорий и предложений:
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
Похоже, свойство категории имеет значение Нет в методе очистки модели предложения, но я не уверен, почему и как я могу передать его там. Я хочу использовать метод очистки модели, поскольку он также охватывает проверку админ-панели django при создании нового объекта. Есть идеи?