-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
34 changed files
with
4,008 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
The `compat` module provides support for backwards compatibility with older | ||
versions of Django, and compatibility wrappers around optional packages. | ||
""" | ||
import inspect | ||
|
||
from django.apps import apps | ||
from django.core.exceptions import ImproperlyConfigured | ||
from django.db import models | ||
|
||
from django.core.validators import DecimalValidator | ||
from django.contrib.postgres import fields as postgres_fields | ||
from django.contrib.postgres.fields import JSONField | ||
|
||
|
||
__all__ = [ | ||
'DecimalValidator', 'postgres_fields', 'JSONField', | ||
'_resolve_model', 'get_related_model', 'get_remote_field', | ||
'value_from_object' | ||
] | ||
|
||
|
||
def _resolve_model(obj): | ||
""" | ||
Resolve supplied `obj` to a Django model class. | ||
`obj` must be a Django model class itself, or a string | ||
representation of one. Useful in situations like GH #1225 where | ||
Django may not have resolved a string-based reference to a model in | ||
another model's foreign key definition. | ||
String representations should have the format: | ||
'appname.ModelName' | ||
""" | ||
if isinstance(obj, str) and len(obj.split('.')) == 2: | ||
app_name, model_name = obj.split('.') | ||
resolved_model = apps.get_model(app_name, model_name) | ||
if resolved_model is None: | ||
msg = "Django did not return a model for {0}.{1}" | ||
raise ImproperlyConfigured(msg.format(app_name, model_name)) | ||
return resolved_model | ||
elif inspect.isclass(obj) and issubclass(obj, models.Model): | ||
return obj | ||
raise ValueError("{0} is not a Django model".format(obj)) | ||
|
||
|
||
def get_related_model(field): | ||
return field.remote_field.model | ||
|
||
|
||
def get_remote_field(field, **kwargs): | ||
return field.remote_field | ||
|
||
|
||
def value_from_object(field, obj): | ||
return field.value_from_object(obj) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Helper functions for mapping model fields to a dictionary of default | ||
keyword arguments that should be used for their equivalent serializer fields. | ||
""" | ||
from django.core import validators | ||
from django.db import models | ||
from django.utils.text import capfirst | ||
|
||
from aiorest_ws.db.orm.django.compat import DecimalValidator | ||
from aiorest_ws.db.orm.django.validators import UniqueValidator | ||
from aiorest_ws.utils.field_mapping import needs_label | ||
|
||
__all__ = [ | ||
'NUMERIC_FIELD_TYPES', 'get_detail_view_name', 'get_field_kwargs', | ||
'get_relation_kwargs', 'get_nested_relation_kwargs', 'get_url_kwargs' | ||
] | ||
|
||
|
||
NUMERIC_FIELD_TYPES = ( | ||
models.IntegerField, models.FloatField, models.DecimalField | ||
) | ||
|
||
|
||
def get_detail_view_name(model): | ||
""" | ||
Given a model class, return the view name to use for URL relationships | ||
that refer to instances of the model. | ||
""" | ||
return '%(model_name)s-detail' % { | ||
'app_label': model._meta.app_label, | ||
'model_name': model._meta.object_name.lower() | ||
} | ||
|
||
|
||
def get_field_kwargs(field_name, model_field): | ||
""" | ||
Creates a default instance of a basic non-relational field. | ||
""" | ||
kwargs = {} | ||
validator_kwarg = list(model_field.validators) | ||
|
||
# The following will only be used by ModelField classes. | ||
# Gets removed for everything else. | ||
kwargs['model_field'] = model_field | ||
|
||
if model_field.verbose_name and needs_label(model_field.verbose_name, field_name): # NOQA | ||
kwargs['label'] = capfirst(model_field.verbose_name) | ||
|
||
if model_field.help_text: | ||
kwargs['help_text'] = model_field.help_text | ||
|
||
max_digits = getattr(model_field, 'max_digits', None) | ||
if max_digits is not None: | ||
kwargs['max_digits'] = max_digits | ||
|
||
decimal_places = getattr(model_field, 'decimal_places', None) | ||
if decimal_places is not None: | ||
kwargs['decimal_places'] = decimal_places | ||
|
||
if isinstance(model_field, models.AutoField) or not model_field.editable: | ||
# If this field is read-only, then return early. | ||
# Further keyword arguments are not valid. | ||
kwargs['read_only'] = True | ||
return kwargs | ||
|
||
if model_field.has_default() or model_field.blank or model_field.null: | ||
kwargs['required'] = False | ||
|
||
is_nullable_field = not isinstance(model_field, models.NullBooleanField) | ||
if model_field.null and is_nullable_field: | ||
kwargs['allow_null'] = True | ||
|
||
if model_field.blank and (isinstance(model_field, models.CharField) or | ||
isinstance(model_field, models.TextField)): | ||
kwargs['allow_blank'] = True | ||
|
||
if isinstance(model_field, models.FilePathField): | ||
kwargs['path'] = model_field.path | ||
|
||
if model_field.match is not None: | ||
kwargs['match'] = model_field.match | ||
|
||
if model_field.recursive is not False: | ||
kwargs['recursive'] = model_field.recursive | ||
|
||
if model_field.allow_files is not True: | ||
kwargs['allow_files'] = model_field.allow_files | ||
|
||
if model_field.allow_folders is not False: | ||
kwargs['allow_folders'] = model_field.allow_folders | ||
|
||
if model_field.choices: | ||
# If this model field contains choices, then return early. | ||
# Further keyword arguments are not valid. | ||
kwargs['choices'] = model_field.choices | ||
return kwargs | ||
|
||
# Our decimal validation is handled in the field code, not validator code. | ||
# (In Django 1.9+ this differs from previous style) | ||
if isinstance(model_field, models.DecimalField) and DecimalValidator: | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if not isinstance(validator, DecimalValidator) | ||
] | ||
|
||
# Ensure that max_length is passed explicitly as a keyword arg, | ||
# rather than as a validator. | ||
max_length = getattr(model_field, 'max_length', None) | ||
if max_length is not None and (isinstance(model_field, models.CharField) or | ||
isinstance(model_field, models.TextField)): | ||
kwargs['max_length'] = max_length | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if not isinstance(validator, validators.MaxLengthValidator) | ||
] | ||
|
||
# Ensure that min_length is passed explicitly as a keyword arg, | ||
# rather than as a validator. | ||
min_length = next(( | ||
validator.limit_value for validator in validator_kwarg | ||
if isinstance(validator, validators.MinLengthValidator) | ||
), None) | ||
if min_length is not None and isinstance(model_field, models.CharField): | ||
kwargs['min_length'] = min_length | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if not isinstance(validator, validators.MinLengthValidator) | ||
] | ||
|
||
# Ensure that max_value is passed explicitly as a keyword arg, | ||
# rather than as a validator. | ||
max_value = next(( | ||
validator.limit_value for validator in validator_kwarg | ||
if isinstance(validator, validators.MaxValueValidator) | ||
), None) | ||
if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): | ||
kwargs['max_value'] = max_value | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if not isinstance(validator, validators.MaxValueValidator) | ||
] | ||
|
||
# Ensure that max_value is passed explicitly as a keyword arg, | ||
# rather than as a validator. | ||
min_value = next(( | ||
validator.limit_value for validator in validator_kwarg | ||
if isinstance(validator, validators.MinValueValidator) | ||
), None) | ||
if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): | ||
kwargs['min_value'] = min_value | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if not isinstance(validator, validators.MinValueValidator) | ||
] | ||
|
||
# URLField does not need to include the URLValidator argument, | ||
# as it is explicitly added in. | ||
if isinstance(model_field, models.URLField): | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if not isinstance(validator, validators.URLValidator) | ||
] | ||
|
||
# EmailField does not need to include the validate_email argument, | ||
# as it is explicitly added in. | ||
if isinstance(model_field, models.EmailField): | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if validator is not validators.validate_email | ||
] | ||
|
||
# SlugField do not need to include the 'validate_slug' argument | ||
if isinstance(model_field, models.SlugField): | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if validator is not validators.validate_slug | ||
] | ||
|
||
# for IPAddressField exclude the 'validate_ipv46_address' argument | ||
if isinstance(model_field, models.GenericIPAddressField): | ||
validator_kwarg = [ | ||
validator for validator in validator_kwarg | ||
if validator is not validators.validate_ipv46_address | ||
] | ||
|
||
if getattr(model_field, 'unique', False): | ||
unique_error_message = model_field.error_messages.get('unique', None) | ||
if unique_error_message: | ||
unique_error_message = unique_error_message % { | ||
'model_name': model_field.model._meta.verbose_name, | ||
'field_label': model_field.verbose_name | ||
} | ||
validator = UniqueValidator( | ||
queryset=model_field.model._default_manager, | ||
message=unique_error_message) | ||
validator_kwarg.append(validator) | ||
|
||
if validator_kwarg: | ||
kwargs['validators'] = validator_kwarg | ||
|
||
return kwargs | ||
|
||
|
||
def get_relation_kwargs(field_name, relation_info): | ||
""" | ||
Creates a default instance of a flat relational field. | ||
""" | ||
model_field = relation_info.model_field | ||
related_model = relation_info.related_model | ||
to_many = relation_info.to_many | ||
to_field = relation_info.to_field | ||
has_through_model = relation_info.has_through_model | ||
|
||
kwargs = { | ||
'queryset': related_model._default_manager, | ||
'view_name': get_detail_view_name(related_model) | ||
} | ||
|
||
if to_many: | ||
kwargs['many'] = True | ||
|
||
if to_field: | ||
kwargs['to_field'] = to_field | ||
|
||
if has_through_model: | ||
kwargs['read_only'] = True | ||
kwargs.pop('queryset', None) | ||
|
||
if model_field: | ||
if model_field.verbose_name and needs_label(model_field, field_name): | ||
kwargs['label'] = capfirst(model_field.verbose_name) | ||
help_text = model_field.help_text | ||
if help_text: | ||
kwargs['help_text'] = help_text | ||
if not model_field.editable: | ||
kwargs['read_only'] = True | ||
kwargs.pop('queryset', None) | ||
if kwargs.get('read_only', False): | ||
# If this field is read-only, then return early. | ||
# No further keyword arguments are valid. | ||
return kwargs | ||
|
||
if model_field.has_default() or model_field.blank or model_field.null: | ||
kwargs['required'] = False | ||
if model_field.null: | ||
kwargs['allow_null'] = True | ||
if model_field.validators: | ||
kwargs['validators'] = model_field.validators | ||
if getattr(model_field, 'unique', False): | ||
queryset = model_field.model._default_manager | ||
validator = UniqueValidator(queryset=queryset) | ||
kwargs['validators'] = kwargs.get('validators', []) + [validator] | ||
if to_many and not model_field.blank: | ||
kwargs['allow_empty'] = False | ||
|
||
return kwargs | ||
|
||
|
||
def get_nested_relation_kwargs(relation_info): | ||
kwargs = {'read_only': True} | ||
if relation_info.to_many: | ||
kwargs['many'] = True | ||
return kwargs | ||
|
||
|
||
def get_url_kwargs(model_field): | ||
return { | ||
'view_name': get_detail_view_name(model_field) | ||
} |
Oops, something went wrong.