我是python(3.5)/django(1.10)的新手,我遇到了以下问题:
我正在使用Django的通用CreateView来创建一个模型和各自的子模型。
我的目标是保存有关购买的信息。一次购买由一张帐单和一张或多张收据组成。为了实现这一点,我创建了一个自定义表单(BillForm
),其中有一个自定义字段,用户可以在其中输入逗号分隔的值,这些值将用于创建收据。
我有:
models.py
class Bill(models.Model):
""" A given bill whenever an item is purchased."""
number = models.CharField(_('Number'), max_length=20)
purchase_date = models.DateTimeField(_('Purchase Date'))
company = models.ForeignKey(Company, verbose_name=_('Company'),
on_delete=models.DO_NOTHING)
...
def _get_receipts(self):
''' returns all receipts that related to a bill '''
return Receipt.objects.filter(bill = self)
receipts = property(_get_receipts)
class Receipt(models.Model):
""" A receipt confirming a product is in our posession """
number = models.CharField(_('Number'), max_length=20)
bill = models.ForeignKey(Bill, verbose_name=_('Bill'),
on_delete=models.DO_NOTHING)
urls . py:
url(_(r'^create_purchase/$'), views.ObjectCreateView.as_view(
model=Bill,
form_class=BillForm
),
name="create_purchase"),
forms.py:
class MultipleReceiptsField(forms.Field):
''' A custom field to store a list of coma separated receipts '''
def to_python(self, value):
''' Normalize data to a set of receipts numbers '''
if not value:
return set()
return set(v.strip() for v in value.split(',') if v.strip())
def validate(self, value):
''' check if the values passed are less than 20 char long (limit for
model). '''
super(MultipleReceiptsField, self).validate(value)
# had made the following a tuple, then updated to list and worked
invalid_receipts = [r for r in value if len(r) > 20]
if invalid_receipts:
# EXCEPTION THROWN HERE BUT NOT PROPAGATED
raise ValidationError(
[ValidationError(_("Length of receipt %(r) is too large (max 20 characters)"),
code=INVALID_PARAMS, params={'r':int(r)}) # it was params={r:r} should have used 'r'
for r in invalid_receipts]
)
class BillForm(forms.ModelForm):
''' A form used to create a Purchase Bill. '''
receipts = MultipleReceiptsField(label=_("Receipts"), widget=forms.Textarea)
def __init__(self, *args, **kwargs):
''' constructor used to filter the companies. '''
# executes
super(BillForm, self).__init__(*args, **kwargs)
self.fields['company'].queryset = Company.objects.filter(is_provider=True).filter(is_active=True)
def save(self, commit=True):
''' createe receipts when saving a bll '''
# save logic | NEVER EXECUTES...
# for each elementt in receipts, create a receipt object and
# set its bill value to the bill that we just created
bill = super(BillForm, self).save(commit=False)
receipts_cd = self.cleaned_data["receipts"]
...
return bill
class Meta:
model = Bill
fields = ('company', "number", "currency", "price", "receipts")
view.py:
class ObjectCreateView(IsInPurchaseGroup, CreateView):
model = None
form_class = None
#fields = "__all__"
template_name = "purchases/object_form.html"
def get_context_data(self, **kwargs):
context = super(ObjectCreateView, self).get_context_data(**kwargs)
context["title"] = str(ACTIONS[CREATE_ACTION]) + " " +
str(self.model._meta.verbose_name)
context["button"] = ACTIONS[CREATE_ACTION]
return context
def get_success_url(self):
next_url, kwargs = get_next_url(self.request)
if not next_url:
# works because the pluralization of both models end with s
next_url =
"purchases:{}s".format((str(self.model).split(".")[-1])[:-2])
# if i am creating a bill, then i must navigate to
# create_purchased_products in the warehouse department.
if self.model == Bill:
next_url = "warehouse:create_purchased_products"
kwargs = {"receipts" : self.object.receipts}
return reverse_lazy(next_url, kwargs=kwargs)
object_form.html
{% extends "purchases/base.html" %}
{% load i18n %}
{% block body %}
<h3>{{ title }}</h3>
{% if error_message %}
<p><strong>{{ error_message }}</strong></p>
{% endif %}
<form action="" method="post">{% csrf_token %}
<table>{{ form.as_table }}</table>
<div class="buttons">
<input type="Submit" value="{{ button }}"/>
</div>
</form>
{% endblock %}
问题:
在url上指定form_class时,以下函数不会被执行:
- BillForm.save ()
- ObjectCreateView.get_success_url ()
如果我省略url上的form_class并设置变量fields = "__all__"
(简单地创建我的Bill模型的对象),那么方法ObjectCreateView.get_success_url()
被调用。
我的问题
为什么BillForm.save()
和ObjectCreateView.get_success_url()
没有被执行?我知道BillForm
一定有问题,但我似乎不明白……
感谢您的帮助。
更新- 上传object_form.html和更新forms.py
- 没有抛出异常。单击Submit后,将向服务器发送一个POST。在客户端(浏览器)上,没有发生任何更改(表单保留已输入的数据)。
- 找到我的错误;但我仍然不确定为什么会这样。
- 我在
MultipleReceiptsField.validate()
中抛出了一些异常,但它们没有得到传播(当我提交表单时,我没有得到异常,表单只是继续发布)。 - 在异常被修复后(看到当放置无效的输入将引发验证错误),并试图提交正确的数据,表单继续转发。 然后我更新了
invalid_receipts
变量从一个元组到一个列表,它开始工作 - 我在
我错过了元组和列表之间的一些微妙的区别吗?
forms.py -不工作:
class MultipleReceiptsField(forms.Field):
''' A custom field to store a list of coma separated receipts '''
def to_python(self, value):
''' Normalize data to a set of receipts numbers '''
if not value:
return set()
return set(v.strip() for v in value.split(',') if v.strip())
def validate(self, value):
''' check if the values passed are less than 20 char long (limit for
model). '''
super(MultipleReceiptsField, self).validate(value)
invalid_receipts = (r for r in value if len(r) > 20)
if invalid_receipts:
raise ValidationError(
[ValidationError(_("Length of recipt %(r) is too large (max 20 characters)"),
code=INVALID_PARAMS, params={'r':int(r)})
for r in invalid_receipts]
)
forms.py - working:
lass MultipleReceiptsField(forms.Field):
''' A custom field to store a list of coma separated receipts '''
def to_python(self, value):
''' Normalize data to a set of receipts numbers '''
if not value:
return set()
return set(v.strip() for v in value.split(',') if v.strip())
def validate(self, value):
''' check if the values passed are less than 20 char long (limit for
model). '''
super(MultipleReceiptsField, self).validate(value)
invalid_receipts = [r for r in value if len(r) > 20]
if invalid_receipts:
raise ValidationError(
[ValidationError(_("Length of receipt %(r) is too large (max 20 characters)"),
code=INVALID_PARAMS, params={'r':int(r)})
for r in invalid_receipts]
)
我认为save方法有问题。方法保存返回对象的实例。如果不能保存,返回None。你的方法总是返回None。在调用其他方法后创建视图检查表单保存结果。如果它没有看到进程中止。你需要返回一些实例或调用super。保存如下:
def save(self, commit=True):
inst = super(BillForm, self).save(commit=False)
#addition logic, all what you need.
return inst
或者您需要在视图类上重写valid方法,而不需要调用super().valid()并在form中重写save方法。
对不起,我的英语很差。