mirror of
https://github.com/pretix/pretix.git
synced 2026-08-26 13:14:40 +00:00
Question: Allow limit of string length (#3214)
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user