1
1
import warnings
2
2
3
+ from django .core .validators import (
4
+ DecimalValidator , EmailValidator , MaxLengthValidator , MaxValueValidator ,
5
+ MinLengthValidator , MinValueValidator , RegexValidator , URLValidator
6
+ )
3
7
from django .db import models
4
8
from django .utils .encoding import force_text
5
9
6
10
from rest_framework import exceptions , serializers
7
11
from rest_framework .compat import uritemplate
12
+ from rest_framework .fields import empty
8
13
9
14
from .generators import BaseSchemaGenerator
10
15
from .inspectors import ViewInspector
@@ -268,18 +273,76 @@ def _map_field(self, field):
268
273
'format' : 'date-time' ,
269
274
}
270
275
276
+ # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification."
277
+ # see: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types
278
+ # see also: https://swagger.io/docs/specification/data-models/data-types/#string
279
+ if isinstance (field , serializers .EmailField ):
280
+ return {
281
+ 'type' : 'string' ,
282
+ 'format' : 'email'
283
+ }
284
+
285
+ if isinstance (field , serializers .URLField ):
286
+ return {
287
+ 'type' : 'string' ,
288
+ 'format' : 'uri'
289
+ }
290
+
291
+ if isinstance (field , serializers .UUIDField ):
292
+ return {
293
+ 'type' : 'string' ,
294
+ 'format' : 'uuid'
295
+ }
296
+
297
+ if isinstance (field , serializers .IPAddressField ):
298
+ content = {
299
+ 'type' : 'string' ,
300
+ }
301
+ if field .protocol != 'both' :
302
+ content ['format' ] = field .protocol
303
+ return content
304
+
305
+ # DecimalField has multipleOf based on decimal_places
306
+ if isinstance (field , serializers .DecimalField ):
307
+ content = {
308
+ 'type' : 'number'
309
+ }
310
+ if field .decimal_places :
311
+ content ['multipleOf' ] = float ('.' + (field .decimal_places - 1 ) * '0' + '1' )
312
+ if field .max_whole_digits :
313
+ content ['maximum' ] = int (field .max_whole_digits * '9' ) + 1
314
+ content ['minimum' ] = - content ['maximum' ]
315
+ self ._map_min_max (field , content )
316
+ return content
317
+
318
+ if isinstance (field , serializers .FloatField ):
319
+ content = {
320
+ 'type' : 'number'
321
+ }
322
+ self ._map_min_max (field , content )
323
+ return content
324
+
<
57AE
td data-grid-cell-id="diff-99d3943485b50b323271fe5399dab8b2c778f42cbc0749b0f702e4cab0a81b2c-270-325-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
325
+ if isinstance (field , serializers .IntegerField ):
326
+ content = {
327
+ 'type' : 'integer'
328
+ }
329
+ self ._map_min_max (field , content )
330
+ return content
331
+
271
332
# Simplest cases, default to 'string' type:
272
333
FIELD_CLASS_SCHEMA_TYPE = {
273
334
serializers .BooleanField : 'boolean' ,
274
- serializers .DecimalField : 'number' ,
275
- serializers .FloatField : 'number' ,
276
- serializers .IntegerField : 'integer' ,
277
-
278
335
serializers .JSONField : 'object' ,
279
336
serializers .DictField : 'object' ,
280
337
}
281
338
return {'type' : FIELD_CLASS_SCHEMA_TYPE .get (field .__class__ , 'string' )}
282
339
340
+ def _map_min_max (self , field , content ):
341
+ if field .max_value :
342
+ content ['maximum' ] = field .max_value
343
+ if field .min_value :
344
+ content ['minimum' ] = field .min_value
345
+
283
346
def _map_serializer (self , serializer ):
284
347
# Assuming we have a valid serializer instance.
285
348
# TODO:
@@ -303,13 +366,51 @@ def _map_serializer(self, serializer):
303
366
schema ['writeOnly' ] = True
304
367
if field .allow_null :
305
368
schema ['nullable' ] = True
369
+ if field .default and field .default != empty : # why don't they use None?!
370
+ schema ['default' ] = field .default
371
+ if field .help_text :
372
+ schema ['description' ] = field .help_text
373
+ self ._map_field_validators (field .validators , schema )
306
374
307
375
properties [field .field_name ] = schema
308
376
return {
309
377
'required' : required ,
310
378
'properties' : properties ,
311
379
}
312
380
381
+ def _map_field_validators (self , validators , schema ):
382
+ """
383
+ map field validators
384
+ :param list:validators: list of field validators
385
+ :param dict:schema: schema that the validators get added to
386
+ """
387
+ for v in validators :
388
+ # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification."
389
+ # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types
390
+ if isinstance (v , EmailValidator ):
391
+ schema ['format' ] = 'email'
392
+ if isinstance (v , URLValidator ):
393
+ schema ['format' ] = 'uri'
394
+ if isinstance (v , RegexValidator ):
395
+ schema ['pattern' ] = v .regex .pattern
396
+ elif isinstance (v , MaxLengthValidator ):
397
+ schema ['maxLength' ] = v .limit_value
398
+ elif isinstance (v , MinLengthValidator ):
399
+ schema ['minLength' ] = v .limit_value
400
+ elif isinstance (v , MaxValueValidator ):
401
+ schema ['maximum' ] = v .limit_value
402
+ elif isinstance (v , MinValueValidator ):
403
+ schema ['minimum' ] = v .limit_value
404
+ elif isinstance (v , DecimalValidator ):
405
+ if v .decimal_places :
406
+ schema ['multipleOf' ] = float ('.' + (v .decimal_places - 1 ) * '0' + '1' )
407
+ if v .max_digits :
408
+ digits = v .max_digits
409
+ if v .decimal_places is not None and v .decimal_places > 0 :
410
+ digits -= v .decimal_places
411
+ schema ['maximum' ] = int (digits * '9' ) + 1
412
+ schema ['minimum' ] = - schema ['maximum' ]
413
+
313
414
def _get_request_body (self , path , method ):
314
415
view = self .view
315
416
0 commit comments