我正在尝试学习Django,但我遇到了一些令人困惑的问题。我目前在使用表单创建电影时遇到问题。表单的想法是为用户提供他想要填写的任何字段。用户填写的任何字段都将在其各自的sql表中更新(将忽略空字段)。但是,当我提交表单时,表单不断向我提供错误“输入值列表”。为了解决这个问题,我认为将表单中的数据填充到列表中,然后返回该列表就可以解决这个问题。
第一个想法是覆盖我的ModelForm中的clean()
。但是,由于表单未能通过is_valid()
检查我的观看次数,cleaned_data
中的clean()
变量不包含任何内容。接下来,我尝试覆盖to_python()
。但是,to_python()
似乎没有被调用。
如果我将__metaclass__ = models.SubfieldBase
放在相应的模型中,我会收到运行时错误
“TypeError:调用时出错 元类基础 元类冲突:派生类的元类必须是a (非严格的)子类 所有基础的元类“
我的方法似乎不起作用。我不确定如何绕过'输入值列表'错误!有什么建议吗?
以下是相关代码(已更新):
models.py
""" Idea:
A movie consists of many equipments, actors, and lighting techniques. It also has a rank for the particular movie, as well as a title.
A Theater consists of many movies.
A nation consists of many theaters.
"""
from django.db import models
from django.contrib.auth.models import User
class EquipmentModel(models.Model):
equip = models.CharField(max_length=20)
# user = models.ForeignKey(User)
class ActorModel(models.Model):
actor = models.CharField(max_length=20)
# user = models.ForeignKey(User)
class LightModel(models.Model):
light = models.CharField(max_length=20)
# user = models.ForeignKey(User)
class MovieModel(models.Model):
# __metaclass__ = models.SubfieldBase
rank = models.DecimalField(max_digits=5000, decimal_places=3)
title = models.CharField(max_length=20)
equipments = models.ManyToManyField(EquipmentModel, blank=True, null=True)
actors = models.ManyToManyField(ActorModel, blank=True, null=True)
lights = models.ManyToManyField(LightModel, blank=True, null=True)
class TheaterModel(models.Model):
movies = models.ForeignKey(MovieModel)
class NationModel(models.Model):
theaters = models.ForeignKey(TheaterModel)
=====================================
forms.py
"""
These Modelforms tie in the models from models.py
Users will be able to write to any of the fields in MovieModel when creating a movie.
Users may leave any field blank (empty fields should be ignored, ie: no updates to database).
"""
from django import forms
from models import MovieModel
from django.forms.widgets import Textarea
class MovieModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MovieModelForm, self).__init__(*args, **kwargs)
self.fields["actors"].widget = Textarea()
self.fields["equipments"].widget = Textarea()
self.fields["lights"].widget = Textarea()
def clean_actors(self):
data = self.cleaned_data.get('actors')
print 'cleaning actors'
return [data]
class Meta:
model = MovieModel
=============================================
views.py
""" This will display the form used to create a MovieModel """
from django.shortcuts import render_to_response
from django.template import RequestContext
from forms import MovieModelForm
def add_movie(request):
if request.method == "POST":
form = MovieModelForm(request.POST)
if form.is_valid():
new_moviemodel = form.save()
return HttpResponseRedirect('/data/')
else:
form = MovieModelForm()
return render_to_response('add_movie_form.html', {form:form,}, context_instance=RequestContext(request))
答案 0 :(得分:11)
可能的问题是文本区域中提供的值列表无法标准化为模型列表。
请参阅ModelMultipleChoiceField documentation。
该字段期望有效ID列表,但可能正在接收文本值列表,django无法转换为实际模型实例。 to_python
将在表单字段中失败,而不是在表单本身内失败。因此,这些价值甚至都达不到形式。
使用内置的ModelMultipleChoiceField有什么问题吗?它将提供最简单的方法,但需要您的用户扫描可用的actor列表(我在这里使用actors字段作为示例)。
在我举例说明我如何尝试做你想做的事之前,我必须要问;你想如何处理数据库中尚不存在的已输入的演员?您可以创建它们(如果存在),也可以失败。你需要对此做出决定。
# only showing the actor example, you can use something like this for other fields too
class MovieModelForm(forms.ModelForm):
actors_list = fields.CharField(required=False, widget=forms.Textarea())
class Meta:
model = MovieModel
exclude = ('actors',)
def clean_actors_list(self):
data = self.cleaned_data
actors_list = data.get('actors_list', None)
if actors_list is not None:
for actor_name in actors_list.split(','):
try:
actor = Actor.objects.get(actor=actor_name)
except Actor.DoesNotExist:
if FAIL_ON_NOT_EXIST: # decide if you want this behaviour or to create it
raise forms.ValidationError('Actor %s does not exist' % actor_name)
else: # create it if it doesnt exist
Actor(actor=actor_name).save()
return actors_list
def save(self, commit=True):
mminstance = super(MovieModelForm, self).save(commit=commit)
actors_list = self.cleaned_data.get('actors_list', None)
if actors_list is not None:
for actor_name in actors_list.split(","):
actor = Actor.objects.get(actor=actor_name)
mminstance.actors.add(actor)
mminstance.save()
return mminstance
以上是所有未经测试的代码,但如果您真的想将Textarea用于ModelMultipleChoiceField,那么接近此代码的方法应该有效。如果你沿着这条路走下去,并且发现我上面的代码中有错误,请编辑我的答案,或者提供评论,以便我可以。祝你好运。
编辑:
另一个选项是创建一个理解逗号分隔的值列表的字段,但其行为方式与ModelMultipleChoiceField类似。查看ModelMultipleChoiceField的源代码,它来自ModelChoiceField,它允许您定义模型上的哪个值用于规范化。
## removed code because it's no longer relevant. See Last Edit ##
修改强>
哇,我真的应该检查一下django trac,看看这是否已经修好了。它是。有关信息,请参阅following ticket。基本上,他们做了同样的事情。他们使ModelMutipleChoiceField尊重to_field_name
参数。 这仅适用于django 1.3!
问题是,常规ModelMultipleChoiceField将看到逗号分隔的字符串,并且因为它不是List或Tuple而失败。因此,我们的工作变得有点困难,因为在常规清理方法可以运行之前,我们必须将字符串更改为列表或元组。
class ModelCommaSeparatedChoiceField(ModelMultipleChoiceField):
widget = Textarea
def clean(self, value):
if value is not None:
value = [item.strip() for item in value.split(",")] # remove padding
return super(ModelCommaSeparatedChoiceField, self).clean(value)
所以,现在你的表单应该是这样的:
class MovieModelForm(forms.ModelForm):
actors = ModelCommaSeparatedChoiceField(
required=False,
queryset=Actor.objects.filter(),
to_field_name='actor')
equipments = ModelCommaSeparatedChoiceField(
required=False,
queryset=Equipment.objects.filter(),
to_field_name='equip')
lights = ModelCommaSeparatedChoiceField(
required=False,
queryset=Light.objects.filter(),
to_field_name='light')
class Meta:
model = MovieModel
答案 1 :(得分:0)
to_python
AFAIK是字段的方法,而不是表单。
clean()
,因此ModelMultipleChoiceFields
clean()
方法会引发验证错误,因此cleaned_data
不包含任何内容。
您没有提供输入什么类型数据的示例,但答案在于表单字段清理。
http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
您需要编写特定于该字段的验证,以便以您的字段所期望的格式返回正确的数据,或者引发ValidationError
,以便您的视图可以使用错误消息重新呈现表单。
更新:您可能错过了ModelForm
__init__
- 看看是否能解决问题。
class MovieModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MovieModelForm, self).__init__(*args, **kwargs)
self.fields["actors"].widget = Textarea()
def clean_actors(self):
data = self.cleaned_data.get('actors')
# validate incoming data. Convert the raw incoming string
# to a list of ids this field is expecting.
# if invalid, raise forms.ValidationError("Error MSG")
return data.split(',') # just an example if data was '1,3,4'