在将ManyToManyField呈现为Textarea时,Django"输入值列表"形式错误

sha*_*fin 9 django

我正在努力学习Django,但我遇到了一些令人困惑的问题.我目前在使用表单创建电影时遇到问题.表单的想法是为用户提供他想要填写的任何字段.用户填写的任何字段都将在其各自的sql表中更新(将忽略空字段).但是,当我提交表单时,表单不断向我提供错误"输入值列表".为了解决这个问题,我认为将表单中的数据填充到列表中,然后返回该列表就可以解决这个问题.

第一个想法是覆盖clean()我的ModelForm.但是,由于表单未能is_valid()在我的视图中进行检查,因此cleaned_data变量in clean()不包含任何内容.接下来,我试图覆盖to_python().但是,to_python()似乎没有被称为.

如果我放入__metaclass__ = models.SubfieldBase相应的模型,我会收到运行时错误

"TypeError:调用元类基类元类冲突时出错:派生类的元类必须是其所有基类的元类的(非严格)子类"

我的方法似乎不起作用.我不确定如何绕过'输入值列表'错误!有什么建议吗?

这是相关代码(更新):

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))
Run Code Online (Sandbox Code Playgroud)

Jos*_*ton 11

可能的问题是文本区域中提供的值列表无法标准化为模型列表.

请参阅ModelMultipleChoiceField文档.

该字段期望有效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
Run Code Online (Sandbox Code Playgroud)

以上是所有未经测试的代码,但是如果您真的想要将Textarea用于ModelMultipleChoiceField,那么接近这个代码应该会有效.如果你沿着这条路走下去,并且发现我上面的代码中有错误,请编辑我的答案,或者提供评论,以便我可以.祝好运.

编辑:

另一个选项是创建一个理解逗号分隔的值列表的字段,但其行为方式与ModelMultipleChoiceField类似.查看ModelMultipleChoiceField的源代码,它来自ModelChoiceField,它允许您定义模型上的哪个值用于规范化.

## removed code because it's no longer relevant. See Last Edit ##
Run Code Online (Sandbox Code Playgroud)

编辑:

哇,我真的应该检查django trac,看看这是否已经修复.它是.有关信息,请参阅以下故障单.基本上,他们做了同样的事情.他们让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)
Run Code Online (Sandbox Code Playgroud)

所以,现在你的表单应该是这样的:

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
Run Code Online (Sandbox Code Playgroud)