api: extend DRF date field to accept empty string (#36365)
This commit is contained in:
parent
96f8538a08
commit
05340b110b
|
@ -33,6 +33,7 @@ from django.template.defaultfilters import capfirst
|
|||
from django.core.files.storage import default_storage
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from .decorators import to_iter
|
||||
from .plugins import collect_from_plugins
|
||||
|
@ -47,9 +48,36 @@ DEFAULT_TITLE_CHOICES = (
|
|||
)
|
||||
|
||||
|
||||
class BirthdateWidget(widgets.DateWidget):
|
||||
class DateWidget(widgets.DateWidget):
|
||||
help_text = _('Format: yyyy-mm-dd')
|
||||
|
||||
|
||||
class DateField(forms.DateField):
|
||||
widget = DateWidget
|
||||
|
||||
|
||||
class DateRestField(serializers.DateField):
|
||||
default_error_messages = {
|
||||
'blank': _('This field may not be blank.'),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.allow_blank = kwargs.pop('allow_blank', False)
|
||||
self.trim_whitespace = kwargs.pop('trim_whitespace', True)
|
||||
super(DateRestField, self).__init__(**kwargs)
|
||||
|
||||
def run_validation(self, data=empty):
|
||||
# Test for the empty string here so that it does not get validated,
|
||||
# and so that subclasses do not need to handle it explicitly
|
||||
# inside the `to_internal_value()` method.
|
||||
if data == '' or (self.trim_whitespace and six.text_type(data).strip() == ''):
|
||||
if not self.allow_blank:
|
||||
self.fail('blank')
|
||||
return ''
|
||||
return super(DateRestField, self).run_validation(data)
|
||||
|
||||
|
||||
class BirthdateWidget(DateWidget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
options = kwargs.setdefault('options', {})
|
||||
options['endDate'] = '-1d'
|
||||
|
@ -69,7 +97,7 @@ class BirthdateField(forms.DateField):
|
|||
]
|
||||
|
||||
|
||||
class BirthdateRestField(serializers.DateField):
|
||||
class BirthdateRestField(DateRestField):
|
||||
default_validators = [
|
||||
validate_birthdate,
|
||||
]
|
||||
|
@ -203,19 +231,16 @@ DEFAULT_ATTRIBUTE_KINDS = [
|
|||
{
|
||||
'label': _('date'),
|
||||
'name': 'date',
|
||||
'field_class': forms.DateField,
|
||||
'kwargs': {
|
||||
'widget': widgets.DateWidget,
|
||||
},
|
||||
'serialize': lambda x: x.isoformat(),
|
||||
'field_class': DateField,
|
||||
'serialize': lambda x: x and x.isoformat(),
|
||||
'deserialize': lambda x: x and datetime.datetime.strptime(x, '%Y-%m-%d').date(),
|
||||
'rest_framework_field_class': serializers.DateField,
|
||||
'rest_framework_field_class': DateRestField,
|
||||
},
|
||||
{
|
||||
'label': _('birthdate'),
|
||||
'name': 'birthdate',
|
||||
'field_class': BirthdateField,
|
||||
'serialize': lambda x: x.isoformat(),
|
||||
'serialize': lambda x: x and x.isoformat(),
|
||||
'deserialize': lambda x: x and datetime.datetime.strptime(x, '%Y-%m-%d').date(),
|
||||
'rest_framework_field_class': BirthdateRestField,
|
||||
},
|
||||
|
|
|
@ -178,6 +178,7 @@ class Attribute(models.Model):
|
|||
|
||||
def get_drf_field(self, **kwargs):
|
||||
from rest_framework import serializers
|
||||
from authentic2.attribute_kinds import DateRestField
|
||||
|
||||
kind = self.get_kind()
|
||||
field_class = kind['rest_framework_field_class']
|
||||
|
@ -198,8 +199,14 @@ class Attribute(models.Model):
|
|||
if (issubclass(field_class, serializers.CharField) and 'allow_blank' not in
|
||||
base_kwargs):
|
||||
base_kwargs['allow_blank'] = True
|
||||
elif (issubclass(field_class, DateRestField) and 'allow_blank' not in
|
||||
base_kwargs):
|
||||
base_kwargs['allow_blank'] = True
|
||||
elif issubclass(field_class, serializers.CharField):
|
||||
base_kwargs['allow_blank'] = False
|
||||
elif issubclass(field_class, DateRestField):
|
||||
base_kwargs['allow_blank'] = False
|
||||
|
||||
base_kwargs.update(kwargs)
|
||||
return field_class(**base_kwargs)
|
||||
|
||||
|
|
|
@ -1378,6 +1378,93 @@ def test_api_user_required_drf_attribute(settings, app, admin, simple_user):
|
|||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid), params=payload, headers=headers, status=200)
|
||||
|
||||
|
||||
def test_api_users_required_date_attributes(settings, app, admin, simple_user):
|
||||
Attribute.objects.create(kind='string', name='prefered_color', label='prefered color', required=True)
|
||||
Attribute.objects.create(kind='date', name='date', label='date', required=True)
|
||||
Attribute.objects.create(kind='birthdate', name='birthdate', label='birthdate', required=True)
|
||||
|
||||
User = get_user_model()
|
||||
payload = {
|
||||
'username': simple_user.username,
|
||||
'id': simple_user.id,
|
||||
'email': 'john.doe@nowhere.null',
|
||||
'first_name': 'Johnny',
|
||||
'last_name': 'Doe',
|
||||
}
|
||||
headers = basic_authorization_header(admin)
|
||||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid),
|
||||
params=payload, headers=headers, status=400)
|
||||
assert resp.json['result'] == 0
|
||||
assert resp.json['errors']['prefered_color'] == ['This field is required.']
|
||||
assert resp.json['errors']['date'] == ['This field is required.']
|
||||
assert resp.json['errors']['birthdate'] == ['This field is required.']
|
||||
|
||||
payload['prefered_color'] = ''
|
||||
payload['date'] = ''
|
||||
payload['birthdate'] = ''
|
||||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid),
|
||||
params=payload, headers=headers, status=400)
|
||||
assert resp.json['result'] == 0
|
||||
assert resp.json['errors']['prefered_color'] == ['This field may not be blank.']
|
||||
assert resp.json['errors']['date'] == ['This field may not be blank.']
|
||||
assert resp.json['errors']['birthdate'] == ['This field may not be blank.']
|
||||
|
||||
payload['prefered_color'] = '?'*257
|
||||
payload['date'] = '0000-00-00'
|
||||
payload['birthdate'] = '1899-12-31'
|
||||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid),
|
||||
params=payload, headers=headers, status=400)
|
||||
assert resp.json['result'] == 0
|
||||
assert resp.json['errors']['prefered_color'] == ['Ensure this field has no more than 256 characters.']
|
||||
assert resp.json['errors']['date'] == ['Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].']
|
||||
assert resp.json['errors']['birthdate'] == ['birthdate must be in the past and greater or equal than 1900-01-01.']
|
||||
|
||||
payload['prefered_color'] = 'blue'
|
||||
payload['date'] = '1515-1-15'
|
||||
payload['birthdate'] = '1900-2-22'
|
||||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid),
|
||||
params=payload, headers=headers, status=200)
|
||||
resp = app.get('/api/users/{}/'.format(simple_user.uuid),
|
||||
headers=headers, status=200)
|
||||
assert resp.json['prefered_color'] == 'blue'
|
||||
assert resp.json['date'] == '1515-01-15'
|
||||
assert resp.json['birthdate'] == '1900-02-22'
|
||||
|
||||
|
||||
def test_api_users_optional_date_attributes(settings, app, admin, simple_user):
|
||||
Attribute.objects.create(kind='string', name='prefered_color', label='prefered color', required=False)
|
||||
Attribute.objects.create(kind='date', name='date', label='date', required=False)
|
||||
Attribute.objects.create(kind='birthdate', name='birthdate', label='birthdate', required=False)
|
||||
|
||||
User = get_user_model()
|
||||
payload = {
|
||||
'username': simple_user.username,
|
||||
'id': simple_user.id,
|
||||
'email': 'john.doe@nowhere.null',
|
||||
'first_name': 'Johnny',
|
||||
'last_name': 'Doe',
|
||||
}
|
||||
headers = basic_authorization_header(admin)
|
||||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid),
|
||||
params=payload, headers=headers, status=200)
|
||||
resp = app.get('/api/users/{}/'.format(simple_user.uuid),
|
||||
headers=headers, status=200)
|
||||
payload['prefered_color'] = None
|
||||
payload['date'] = None
|
||||
payload['birthdate'] = None
|
||||
|
||||
payload['prefered_color'] = ''
|
||||
payload['date'] = ''
|
||||
payload['birthdate'] = ''
|
||||
resp = app.put_json('/api/users/{}/'.format(simple_user.uuid),
|
||||
params=payload, headers=headers, status=200)
|
||||
resp = app.get('/api/users/{}/'.format(simple_user.uuid),
|
||||
headers=headers, status=200)
|
||||
payload['prefered_color'] = None
|
||||
payload['date'] = None
|
||||
payload['birthdate'] = None
|
||||
|
||||
|
||||
def test_filter_users_by_service(app, admin, simple_user, role_random, service):
|
||||
app.authorization = ('Basic', (admin.username, admin.username))
|
||||
|
||||
|
|
Loading…
Reference in New Issue