Skip to content

Commit

Permalink
Merge fb62dd1 into abce03d
Browse files Browse the repository at this point in the history
  • Loading branch information
Relrin committed Nov 23, 2016
2 parents abce03d + fb62dd1 commit 18fbaf5
Show file tree
Hide file tree
Showing 38 changed files with 5,987 additions and 36 deletions.
1 change: 1 addition & 0 deletions aiorest_ws/conf/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
UNICODE_JSON = True
COMPACT_JSON = True
COERCE_DECIMAL_TO_STRING = True
UPLOADED_FILES_USE_URL = True

# -----------------------------------------------
# Database
Expand Down
53 changes: 53 additions & 0 deletions aiorest_ws/db/orm/django/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- 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.contrib.postgres import fields as postgres_fields
from django.contrib.postgres.fields import JSONField


__all__ = [
'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)
270 changes: 270 additions & 0 deletions aiorest_ws/db/orm/django/field_mapping.py
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.validators import DecimalValidator, MinLengthValidator, \
MaxLengthValidator, MinValueValidator, MaxValueValidator, URLValidator, \
validate_email, validate_slug, validate_ipv46_address
from django.db import models
from django.utils.text import capfirst


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.
if isinstance(model_field, models.DecimalField):
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, 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, 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, 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, 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, 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, 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, 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, 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 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 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 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)
if model_field.help_text:
kwargs['help_text'] = model_field.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_class):
return {
'view_name': get_detail_view_name(model_class)
}
Loading

0 comments on commit 18fbaf5

Please sign in to comment.