【问题标题】:Manually Rendering Django Form with Validation使用验证手动渲染 Django 表单
【发布时间】:2019-07-08 03:24:26
【问题描述】:

我已经开始创建一个表单。基本上,表格是二十个单词的“测试”。该表单由二十个文本字段组成,我想包含一个单词的定义。用户将输入单词。完成后,表格应验证数据并标记正确和错误。我在 django 中做了很多模型形式,但这个不同。此表单中的所有数据都必须作为上下文传递。
views.py

def get_test(request, username='default'):
    template_name = 'main/test.html'
    if request.method == 'POST':
        pass
    else:
        lang = Language(config('USER'), config('PASS'))
        streakinfo = lang.get_streak_info()
        uniquewords = lang.get_unique_words()
        testwords = get_test_words(uniquewords)
        wordsdict = get_word_dict(testwords)
        form = TestForm()
        context = {
            'testwords': testwords, # list of random unique test words
            'wordsdict': wordsdict, # dict of words + definitions {word: {pronounciation, definition}}
            'form': form,
        }
    return render(request, template_name, context)

forms.py

class TestForm(forms.Form):
    word_1 = forms.CharField(label='1', max_length=100)
    word_2 = forms.CharField(label='2', max_length=100)
    word_3 = forms.CharField(label='3', max_length=100)
    word_4 = forms.CharField(label='4', max_length=100)
    word_5 = forms.CharField(label='5', max_length=100)
    word_6 = forms.CharField(label='6', max_length=100)
    word_7 = forms.CharField(label='7', max_length=100)
    word_8 = forms.CharField(label='8', max_length=100)
    word_9 = forms.CharField(label='9', max_length=100)
    word_10 = forms.CharField(label='10', max_length=100)
    word_11 = forms.CharField(label='11', max_length=100)
    word_12 = forms.CharField(label='12', max_length=100)
    word_13 = forms.CharField(label='13', max_length=100)
    word_14 = forms.CharField(label='14', max_length=100)
    word_15 = forms.CharField(label='15', max_length=100)
    word_16 = forms.CharField(label='16', max_length=100)
    word_17 = forms.CharField(label='17', max_length=100)
    word_18 = forms.CharField(label='18', max_length=100)
    word_19 = forms.CharField(label='19', max_length=100)
    word_20 = forms.CharField(label='20', max_length=100)

我的意思是,手动浏览和渲染每个字段很简单,但我不知道并且从未做过的是没有模型。例如,我想建立一个表,第 1 列有定义(我实际上不需要label=##,因为我再次将数据作为上下文传递),第 2 列有字段。我如何将发布的数据联系在一起,以便在发布结果时,最有把握地将 col 2 与 col 1 进行检查?简而言之,如何手动呈现和验证表单并保持所有数据对齐?如果这是一个广泛的问题,我提前道歉。

更新:

我能够将测试数据放入表单并使用以下 (by hacking away at the forms.Form inheritance) 呈现字段:

class TestForm(forms.Form):
    """
    Student test form
    """    
    def __init__(self, testdict, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.testdict = {} if testdict is None else testdict
        d = self.testdict
        for word in d:
            answer = word
            for key in d[word]:
                value = str(d[word][key])
                if key == 'id':
                    field_name = value
                if key == 'definition':
                    question = value
            self.fields[field_name] = forms.CharField(label=question, max_length=100)

仍然需要帮助。

【问题讨论】:

  • 您需要验证在此表单中输入的数据吗?这就是你要问的,对吧?
  • 第 2 列是什么?另外,您能否发布您已经尝试过的内容(在模板中)?只需在模板中使用{{ form }},您就会对 django 视图的预期有所了解。
  • @ans2human 这是其中的一部分,是的。
  • 知道了,花点时间回答。
  • @ans2human col 2 是字段。用户将用问题的答案填写该字段。第 1 列是问题。

标签: django django-templates


【解决方案1】:

是的,您可以覆盖 clean mehtod 并对它们进行验证,如下所示:

您已经使用表单类编写了所有字段,然后使用 get 您实际上通过它们的name HTML 参数获取这些字段中输入的内容,然后您通过它们的变量来操作数据,如果它们不匹配你到底想要什么然后提出Validationerror。然后,您将这些字段中的所有数据的上下文创建到一个 dict 中,并为其设置一个变量,最后返回该变量。

forms.py

class TestForm(forms.Form):
    word_1 = forms.CharField(label='1', max_length=100)
    word_2 = forms.CharField(label='2', max_length=100)
    word_3 = forms.CharField(label='3', max_length=100)
    word_4 = forms.CharField(label='4', max_length=100)
    word_5 = forms.CharField(label='5', max_length=100)
    word_6 = forms.CharField(label='6', max_length=100)
    word_7 = forms.CharField(label='7', max_length=100)
    word_8 = forms.CharField(label='8', max_length=100)
    word_9 = forms.CharField(label='9', max_length=100)
    word_10 = forms.CharField(label='10', max_length=100)
    word_11 = forms.CharField(label='11', max_length=100)
    word_12 = forms.CharField(label='12', max_length=100)
    word_13 = forms.CharField(label='13', max_length=100)
    word_14 = forms.CharField(label='14', max_length=100)
    word_15 = forms.CharField(label='15', max_length=100)
    word_16 = forms.CharField(label='16', max_length=100)
    word_17 = forms.CharField(label='17', max_length=100)
    word_18 = forms.CharField(label='18', max_length=100)
    word_19 = forms.CharField(label='19', max_length=100)
    word_20 = forms.CharField(label='20', max_length=100)


    def clean(self):

        word_1 = self.cleaned_data.get("word_1")
             #        |
             #        |         write the clean method of all fields
             #        |
             #      ----- 
             #       --- 
             #        - 

        word_20 = self.cleaned_data.get("word_20")



        if word_1 and word_2 and word_7 and word_15 != something:
            raise forms.ValidationError("Something Fishy")
            # i combined few of the word fields but you check all the fields separately also and implement your validation.

        words_context = {
            'word_1':word_1

            #     |               <--write all the context of corresponding fields
            #     |

            'word_20':word_20
        }

        return words_context

Views.py

def get_test(request, username='default'):
    template_name = 'main/test.html'
    form = TestForm()
    if request.method == 'POST':
        if form.is_valid():
            word_1 = self.cleaned_data.get("word_1")
             #        |
             #        |         write the clean method of all fields
             #        |
             #      ----- 
             #       --- 
             #        - 
            word_20 = self.cleaned_data.get("word_20")
            newtest = Test(word_1=word_1,....word_20=word_20)
            newtest.save()
            return redirect('whereever you want to redirect')
    else:
        lang = Language(config('USER'), config('PASS'))
        streakinfo = lang.get_streak_info()
        uniquewords = lang.get_unique_words()
        testwords = get_test_words(uniquewords)
        wordsdict = get_word_dict(testwords)
        form = TestForm()
        context = {
            'testwords': testwords, # list of random unique test words
            'wordsdict': wordsdict, # dict of words + definitions {word: {pronounciation, definition}}
            'form': form,
        }
    return render(request, template_name, context)

【讨论】:

  • 我如何将字段和数据绑定在一起?换句话说,我如何确保 word_1 是 word_1 而 word_1 的答案是 word_1 的答案?我假设我在视图中实例化它时将值传递给 TestForm 类。但是然后呢?
  • 我想这会让我开始。还没有看到 col1 将如何与 col2 绑定,但可能会在测试中弄清楚。
  • 当然,如果解决方案对您有帮助,请不要忘记支持其他读者。
  • 毫无疑问。一旦我回到我的办公桌,就到了游戏时间。 :)
  • 我认为,正如我所怀疑的那样,我的问题太宽泛了。我怀疑这可以很好地处理表单提交(这肯定有帮助),但我不太了解表单的生成。例如,wordsdict 是一个随机生成的字典。 {word: {id, pronounciation, definition}} 是字典的格式。理想情况下,单词的id 将与表单的字段相关联,例如name。表单标签也需要绑定到表单字段,并且表单标签是“测试问题”。如果我只是在这里增加混乱,lmk 和我将重做 OP。
【解决方案2】:

我完成了这两种方式:一种包括文件写入,另一种包括模型写入。由于写入模型显然更快,我将展示:

在我看来,这很简单。这里的开始是当我在GET 请求form = TestForm(wordsdict) 上实例化表单时,我将字典传递给表单。 POST 请求数据从未实际存储,仅用于验证。所以当我 POST 时,我只是像往常一样发送 POST 数据。 wordsdict 是一个由 {answer: [question, id]}

组成的字典

views.py

def language_test(request, username='johndoe', password=None):
    lang= Language(config('USER'), config('PASS'))
    streakinfo = lang.get_streak_info()
    context = {
        'username': username,
        'streakinfo': streakinfo,
    }
    template_name = 'tests/test.html'
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the saved answer dictionary:
        print('POSTING TEST RESULTS')
        form = TestForm(data=request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            print('PASSED')
            # redirect to a new URL:
            return redirect('main:success')
        else:  
            print('FAILED')
            if form.has_error:
                print('FORM ERROR')
            pass
    # if a GET (or any other method) we'll create a blank form
    else:
        print('GETTING NEW TEST')
        phrases = lang.get_known_phrases()
        testwords = get_test_words(phrases)
        wordsdict = get_word_dict(testwords)
        form = TestForm(wordsdict)
    context['form'] = form
    return render(request, template_name, context)

再往前走……

models.py

class TestAnswers(models.Model):
    phraseid = models.IntegerField(unique=True, blank=True, null=True)
    question = models.TextField(blank=True, null=True)
    answer = models.CharField(max_length=50, blank=True, null=True)

这就是魔法发生的地方。我正在对继承的 Form 类的 __init__ 函数进行超级处理。当类被实例化时,它将评估test_dict 参数,该参数可能已由视图传入,也可能未传入。如果没有test_dict,那肯定是请求新的测试,所以我清除测试模型,并用视图传入的随机选择的问题\答案创建一个新的模型。如果没有传入test_dict,那么它一定是一个post请求,意味着我需要验证所有的答案。请参阅表单验证的 clean 方法。

forms.py

class TestForm(forms.Form):
    """
    Student test form
    """    
    def __init__(self, test_dict=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._resource_path = os.path.join(settings.BASE_DIR, 'static/json')
        self._json_path = os.path.join(self._resource_path, 'answers.json')
        self._model = TestAnswers
        i = 0
        phraseid, answer, question = [], [], []
        if test_dict is not None:
        # A form get request should resolve new form data and
        # store it in the database for comparison later on
            # clear out the answers table
            self._model.objects.all().delete()
            # create a list of model objects to bulk insert
            records = []
            for item in test_dict:
                record = self._model(
                    phraseid=test_dict[item]['id'],
                    answer=item,
                    question=test_dict[item]['definition']
                )
                phraseid.append(test_dict[item]['id'])
                question.append(test_dict[item]['definition'])
                answer.append(item)
                records.append(record)
            if records:
                # Insert the records into the TestAnswers table
                self._model.objects.bulk_create(records)
            self.test_dict = test_dict

        else:
        # A form post request should check the form data against
        # what was established during the get request
            # Get all the objects in the test table
            records = self._model.objects.all()
            # Put all the object items into their respective lists
            for r in records:
                phraseid.append(r.phraseid)
                answer.append(r.answer)
                question.append(r.question)
        for i in range(len(question)):
            # Set the form fields
            field_name = 'testword' + str(phraseid[i])
            # Print the answers for debugging
            print('id: ' + str(phraseid[i]))
            print('question: ' + question[i])
            print('answer:' + answer[i])
            self.fields[field_name] = forms.CharField(label=question[i], max_length=100)
        self.question = question
        self.phraseid = phraseid
        self.answer = answer

    def clean(self):
        # print('CLEANING DATA')
        phraseid, answer, question = [], [], []
        context = {}
        i = 0
        records = self._model.objects.all()
        for r in records:
            phraseid.append(r.phraseid)
            answer.append(r.answer)
            question.append(r.question)
        # Get and check the results
        for i in range(len(self.cleaned_data)):
            field_name = 'testword' + str(phraseid[i])
            result = self.cleaned_data.get(field_name)
            if result != answer[i]:
                self.add_error(field_name, 'Incorrect')
            context[i] = question[i]
            i += 1
        return context

【讨论】:

    猜你喜欢
    • 2020-08-16
    • 2017-06-02
    • 2019-04-01
    • 2017-09-15
    • 2020-10-19
    • 2016-04-08
    • 2020-05-08
    • 2014-09-23
    • 1970-01-01
    相关资源
    最近更新 更多