Question: Allow limit of string length (#3214)

This commit is contained in:
Raphael Michel
2023-04-06 09:58:50 +02:00
committed by GitHub
parent b1e8e072d4
commit ddade60625
9 changed files with 47 additions and 2 deletions
+5
View File
@@ -63,6 +63,7 @@ valid_date_max date Maximum value f
valid_datetime_min datetime Minimum value for date and time questions (optional) valid_datetime_min datetime Minimum value for date and time questions (optional)
valid_datetime_max datetime Maximum value for date and time questions (optional) valid_datetime_max datetime Maximum value for date and time questions (optional)
valid_file_portrait boolean Turn on file validation for portrait photos valid_file_portrait boolean Turn on file validation for portrait photos
valid_string_length_max integer Maximum length for string questions (optional)
dependency_question integer Internal ID of a different question. The current dependency_question integer Internal ID of a different question. The current
question will only be shown if the question given in question will only be shown if the question given in
this attribute is set to the value given in this attribute is set to the value given in
@@ -122,6 +123,7 @@ Endpoints
"valid_date_max": null, "valid_date_max": null,
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_string_length_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"dependency_question": null, "dependency_question": null,
"dependency_value": null, "dependency_value": null,
@@ -201,6 +203,7 @@ Endpoints
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"valid_string_length_max": null,
"dependency_question": null, "dependency_question": null,
"dependency_value": null, "dependency_value": null,
"dependency_values": [], "dependency_values": [],
@@ -302,6 +305,7 @@ Endpoints
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"valid_string_length_max": null,
"options": [ "options": [
{ {
"id": 1, "id": 1,
@@ -384,6 +388,7 @@ Endpoints
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"valid_string_length_max": null,
"options": [ "options": [
{ {
"id": 1, "id": 1,
+1 -1
View File
@@ -442,7 +442,7 @@ class QuestionSerializer(I18nAwareModelSerializer):
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_values', 'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min', 'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max', 'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max',
'valid_file_portrait') 'valid_string_length_max', 'valid_file_portrait')
def validate_identifier(self, value): def validate_identifier(self, value):
Question._clean_identifier(self.context['event'], value, self.instance) Question._clean_identifier(self.context['event'], value, self.instance)
+2
View File
@@ -747,12 +747,14 @@ class BaseQuestionsForm(forms.Form):
elif q.type == Question.TYPE_STRING: elif q.type == Question.TYPE_STRING:
field = forms.CharField( field = forms.CharField(
label=label, required=required, label=label, required=required,
max_length=q.valid_string_length_max,
help_text=help_text, help_text=help_text,
initial=initial.answer if initial else None, initial=initial.answer if initial else None,
) )
elif q.type == Question.TYPE_TEXT: elif q.type == Question.TYPE_TEXT:
field = forms.CharField( field = forms.CharField(
label=label, required=required, label=label, required=required,
max_length=q.valid_string_length_max,
help_text=help_text, help_text=help_text,
widget=forms.Textarea, widget=forms.Textarea,
initial=initial.answer if initial else None, initial=initial.answer if initial else None,
@@ -0,0 +1,18 @@
# Generated by Django 3.2.18 on 2023-04-05 10:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0236_reusable_media'),
]
operations = [
migrations.AddField(
model_name='question',
name='valid_string_length_max',
field=models.PositiveIntegerField(null=True),
),
]
+14 -1
View File
@@ -45,7 +45,9 @@ import dateutil.parser
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator, RegexValidator from django.core.validators import (
MaxLengthValidator, MinValueValidator, RegexValidator,
)
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.utils import formats from django.utils import formats
@@ -1540,6 +1542,11 @@ class Question(LoggedModel):
valid_datetime_max = models.DateTimeField(null=True, blank=True, valid_datetime_max = models.DateTimeField(null=True, blank=True,
verbose_name=_('Maximum value'), verbose_name=_('Maximum value'),
help_text=_('Currently not supported in our apps and during check-in')) help_text=_('Currently not supported in our apps and during check-in'))
valid_string_length_max = models.PositiveIntegerField(null=True, blank=True,
verbose_name=_('Maximum length'),
help_text=_(
'Currently not supported in our apps and during check-in'
))
valid_file_portrait = models.BooleanField( valid_file_portrait = models.BooleanField(
default=False, default=False,
verbose_name=_('Validate file to be a portrait'), verbose_name=_('Validate file to be a portrait'),
@@ -1686,6 +1693,12 @@ class Question(LoggedModel):
return answer return answer
else: else:
raise ValidationError(_('Unknown country code.')) raise ValidationError(_('Unknown country code.'))
elif self.type in (Question.TYPE_STRING, Question.TYPE_TEXT):
if self.valid_string_length_max is not None and len(answer) > self.valid_string_length_max:
raise ValidationError(MaxLengthValidator.message % {
'limit_value': self.valid_string_length_max,
'show_value': len(answer)
})
return answer return answer
+1
View File
@@ -168,6 +168,7 @@ class QuestionForm(I18nModelForm):
'valid_date_min', 'valid_date_min',
'valid_date_max', 'valid_date_max',
'valid_file_portrait', 'valid_file_portrait',
'valid_string_length_max',
] ]
widgets = { widgets = {
'valid_datetime_min': SplitDateTimePickerWidget(), 'valid_datetime_min': SplitDateTimePickerWidget(),
@@ -44,6 +44,9 @@
{% bootstrap_field form.valid_datetime_min layout="control" %} {% bootstrap_field form.valid_datetime_min layout="control" %}
{% bootstrap_field form.valid_datetime_max layout="control" %} {% bootstrap_field form.valid_datetime_max layout="control" %}
</div> </div>
<div id="valid-string">
{% bootstrap_field form.valid_string_length_max layout="control" %}
</div>
<div id="valid-file"> <div id="valid-file">
{% bootstrap_field form.valid_file_portrait layout="control" %} {% bootstrap_field form.valid_file_portrait layout="control" %}
</div> </div>
+1
View File
@@ -2066,6 +2066,7 @@ TEST_QUESTION_RES = {
"valid_datetime_min": None, "valid_datetime_min": None,
"valid_datetime_max": None, "valid_datetime_max": None,
"valid_file_portrait": False, "valid_file_portrait": False,
"valid_string_length_max": None,
"help_text": {"en": "This is an example question"}, "help_text": {"en": "This is an example question"},
"options": [ "options": [
{ {
+2
View File
@@ -2693,6 +2693,7 @@ class SeatingTestCase(TestCase):
@pytest.mark.parametrize("qtype,answer,expected", [ @pytest.mark.parametrize("qtype,answer,expected", [
(Question.TYPE_STRING, "a", "a"), (Question.TYPE_STRING, "a", "a"),
(Question.TYPE_TEXT, "v", "v"), (Question.TYPE_TEXT, "v", "v"),
(Question.TYPE_TEXT, "waaaaay tooooo long", ValidationError),
(Question.TYPE_NUMBER, "0.9", ValidationError), (Question.TYPE_NUMBER, "0.9", ValidationError),
(Question.TYPE_NUMBER, "1", Decimal("1")), (Question.TYPE_NUMBER, "1", Decimal("1")),
(Question.TYPE_NUMBER, "3", Decimal("3")), (Question.TYPE_NUMBER, "3", Decimal("3")),
@@ -2745,6 +2746,7 @@ def test_question_answer_validation(qtype, answer, expected):
valid_datetime_max=datetime.datetime(2018, 1, 16, 16, 0, 0, tzinfo=tzoffset(None, 3600)), valid_datetime_max=datetime.datetime(2018, 1, 16, 16, 0, 0, tzinfo=tzoffset(None, 3600)),
valid_number_min=Decimal('1'), valid_number_min=Decimal('1'),
valid_number_max=Decimal('100'), valid_number_max=Decimal('100'),
valid_string_length_max=8,
) )
if isinstance(expected, type) and issubclass(expected, Exception): if isinstance(expected, type) and issubclass(expected, Exception):
with pytest.raises(expected): with pytest.raises(expected):