Skip to content

Commit

Permalink
Merge pull request #275 from uoxiu/Betalos-master
Browse files Browse the repository at this point in the history
Support latest [email protected] and [email protected] from Betalos master
  • Loading branch information
Nicolae Godina authored Jan 2, 2020
2 parents 95c3fe8 + 95d93ab commit 4d32ee1
Show file tree
Hide file tree
Showing 11 changed files with 84 additions and 71 deletions.
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ services: mongodb
sudo: false

env:
- TOX_ENV=dj111-py36-me016
- TOX_ENV=dj111-py36-me018
- TOX_ENV=dj22-py36-me016
- TOX_ENV=dj22-py36-me018
- TOX_ENV=dj2-py36-me019
- TOX_ENV=dj30-py36-me019

matrix:
fast_finish: true
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ The features and differences of this package are described in [API documentation

## Requirements

* Django == 1.* | 2.*
* Django == 2.* | 3.0
* djangorestframework == 3.*
* mongoengine == 0.16.* | 0.17.* | 0.18.*
* mongoengine == 0.18.* | 0.19.*
* blinker == 1.* (for mongoengine referencefields to work)

## Installation
Expand Down Expand Up @@ -93,6 +93,6 @@ Documentation available [here](https://github.com/umutbozkurt/django-rest-framew
@qwiglydee
@BurkovBA
@Vayel
@uixou
@uoxiu

Feel free to mail me if you consider being a maintainer.
1 change: 0 additions & 1 deletion requirements/requirements-testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ pytest
pytest-cov
pytest-django
mock
six
pytz
32 changes: 16 additions & 16 deletions rest_framework_mongoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

from bson import DBRef, ObjectId
from bson.errors import InvalidId
from django.utils import six
from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _
from mongoengine import fields as me_fields
from django.utils.encoding import smart_str
from django.utils.translation import gettext_lazy as _
from mongoengine import Document, EmbeddedDocument
from mongoengine import fields as me_fields
from mongoengine.base import get_document
from mongoengine.base.common import _document_registry
from mongoengine.errors import ValidationError as MongoValidationError
from mongoengine.errors import DoesNotExist, NotRegistered
from mongoengine.errors import ValidationError as MongoValidationError
from mongoengine.queryset import QuerySet, QuerySetManager
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import empty, html
from rest_framework.fields import empty
from rest_framework.utils import html
from rest_framework.settings import api_settings


Expand All @@ -23,12 +23,12 @@ class ObjectIdField(serializers.Field):

def to_internal_value(self, value):
try:
return ObjectId(smart_text(value))
return ObjectId(smart_str(value))
except InvalidId:
raise serializers.ValidationError("'%s' is not a valid ObjectId" % value)

def to_representation(self, value):
return smart_text(value)
return smart_str(value)


class DocumentField(serializers.Field):
Expand Down Expand Up @@ -60,13 +60,13 @@ def to_representation(self, obj):
DRF ModelField uses ``value_to_string`` for this purpose. Mongoengine fields do not have such method.
This implementation uses ``django.utils.encoding.smart_text`` to convert everything to text, while keeping json-safe types intact.
This implementation uses ``django.utils.encoding.smart_str`` to convert everything to text, while keeping json-safe types intact.
NB: The argument is whole object, instead of attribute value. This is upstream feature.
Probably because the field can be represented by a complicated method with nontrivial way to extract data.
"""
value = self.model_field.__get__(obj, None)
return smart_text(value, strings_only=True)
return smart_str(value, strings_only=True)

def run_validators(self, value):
""" validate value.
Expand Down Expand Up @@ -121,7 +121,7 @@ class GenericField(serializers.Field):
""" Field for generic values.
Recursively traverses lists and dicts.
Primitive values are serialized using ``django.utils.encoding.smart_text`` (keeping json-safe intact).
Primitive values are serialized using ``django.utils.encoding.smart_str`` (keeping json-safe intact).
Embedded documents handled using temporary GenericEmbeddedField.
No validation performed.
Expand All @@ -143,7 +143,7 @@ def represent_data(self, data):
elif data is None:
return None
else:
return smart_text(data, strings_only=True)
return smart_str(data, strings_only=True)

def to_internal_value(self, value):
return self.parse_data(value)
Expand Down Expand Up @@ -250,7 +250,7 @@ def choices(self):

return OrderedDict([
(
six.text_type(self.to_representation(item)),
str(self.to_representation(item)),
self.display_value(item)
)
for item in queryset
Expand All @@ -261,7 +261,7 @@ def grouped_choices(self):
return self.choices

def display_value(self, instance):
return six.text_type(instance)
return str(instance)

def parse_id(self, value):
try:
Expand Down Expand Up @@ -529,7 +529,7 @@ def to_internal_value(self, data):
api_settings.NON_FIELD_ERRORS_KEY: [message]
})
return {
six.text_type(key): self.child.run_validation(value)
str(key): self.child.run_validation(value)
for key, value in data.items()
}

Expand All @@ -547,7 +547,7 @@ class FileField(serializers.FileField):
"""

def to_representation(self, value):
return smart_text(value.grid_id) if hasattr(value, 'grid_id') else None
return smart_str(value.grid_id) if hasattr(value, 'grid_id') else None


class ImageField(FileField):
Expand Down
3 changes: 1 addition & 2 deletions rest_framework_mongoengine/repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import re

from django.utils import six
from django.utils.encoding import force_str
from mongoengine.base import BaseDocument
from mongoengine.fields import BaseField
Expand All @@ -33,7 +32,7 @@ def mongo_field_repr(value):
def mongo_doc_repr(value):
# mimic django models.Model.__repr__
try:
u = six.text_type(value)
u = str(value)
except (UnicodeEncodeError, UnicodeDecodeError):
u = '[Bad Unicode data]'
return force_str('<%s: %s>' % (value.__class__.__name__, u))
Expand Down
3 changes: 2 additions & 1 deletion rest_framework_mongoengine/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class MongoRouterMixin(object):
Determines base_name from mongo queryset
"""
def get_default_base_name(self, viewset):

def get_default_basename(self, viewset):
queryset = getattr(viewset, 'queryset', None)
assert queryset is not None, ('`base_name` argument not specified, and could '
'not automatically determine the name from the viewset, as '
Expand Down
5 changes: 2 additions & 3 deletions rest_framework_mongoengine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import warnings
from collections import OrderedDict, namedtuple

from django.utils.six import get_unbound_function
from mongoengine import fields as me_fields
from mongoengine.errors import ValidationError as me_ValidationError
from rest_framework import fields as drf_fields
Expand Down Expand Up @@ -225,7 +224,7 @@ def recursive_save(self, validated_data, instance=None):

# for EmbeddedDocumentSerializers, call recursive_save
if isinstance(field, EmbeddedDocumentSerializer):
me_data[key] = field.recursive_save(value)
me_data[key] = field.recursive_save(value) if value is not None else value # issue when the value is none

# same for lists of EmbeddedDocumentSerializers i.e.
# ListField(EmbeddedDocumentField) or EmbeddedDocumentListField
Expand Down Expand Up @@ -534,7 +533,7 @@ def get_customization_for_nested_field(self, field_name):
nested_validate_methods = {}
for attr in dir(self.__class__):
if attr.startswith('validate_%s__' % field_name.replace('.', '__')):
method = get_unbound_function(getattr(self.__class__, attr))
method = getattr(self.__class__, attr)
method_name = 'validate_' + attr[len('validate_%s__' % field_name.replace('.', '__')):]
nested_validate_methods[method_name] = method

Expand Down
46 changes: 34 additions & 12 deletions rest_framework_mongoengine/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@


class MongoValidatorMixin():
def exclude_current_instance(self, queryset):
if self.instance is not None:
return queryset.filter(pk__ne=self.instance.pk)
def exclude_current_instance(self, queryset, instance):
if instance is not None:
return queryset.filter(pk__ne=instance.pk)
return queryset


Expand All @@ -19,10 +19,26 @@ class UniqueValidator(MongoValidatorMixin, validators.UniqueValidator):
Used by :class:`DocumentSerializer` for fields, present in unique indexes.
"""
def __call__(self, value):

def __init__(self, queryset, message=None, lookup=''):
"""
Setting empty string as default lookup for UniqueValidator.
For Mongoengine exact is a shortcut to query with regular experission.
This fixes https://github.com/umutbozkurt/django-rest-framework-mongoengine/issues/264
"""
super(UniqueValidator, self).__init__(queryset, message, lookup)

def __call__(self, value, serializer_field):
# Determine the underlying model field name. This may not be the
# same as the serializer field name if `source=<>` is set.
field_name = serializer_field.source_attrs[-1]
# Determine the existing instance, if this is an update operation.
instance = getattr(serializer_field.parent, 'instance', None)

queryset = self.queryset
queryset = self.filter_queryset(value, queryset)
queryset = self.exclude_current_instance(queryset)
queryset = self.filter_queryset(value, queryset, field_name)
queryset = self.exclude_current_instance(queryset, instance)

if queryset.first():
raise ValidationError(self.message.format())

Expand All @@ -38,14 +54,19 @@ class UniqueTogetherValidator(MongoValidatorMixin, validators.UniqueTogetherVali
Used by :class:`DocumentSerializer` for fields, present in unique indexes.
"""
def __call__(self, attrs):
def __call__(self, attrs, serializer):
try:
self.enforce_required_fields(attrs)
self.enforce_required_fields(attrs, serializer)
except SkipField:
return

# Determine the existing instance, if this is an update operation.
instance = getattr(serializer, 'instance', None)

queryset = self.queryset
queryset = self.filter_queryset(attrs, queryset)
queryset = self.exclude_current_instance(queryset)
queryset = self.filter_queryset(attrs, queryset, serializer)
queryset = self.exclude_current_instance(queryset, instance)

# Ignore validation if any field is None
checked_values = [
value for field, value in attrs.items() if field in self.fields
Expand All @@ -66,9 +87,10 @@ class OptionalUniqueTogetherValidator(UniqueTogetherValidator):
"""
This validator passes validation if all of validation fields are missing. (for use with partial data)
"""
def enforce_required_fields(self, attrs):

def enforce_required_fields(self, attrs, serializer):
try:
super(OptionalUniqueTogetherValidator, self).enforce_required_fields(attrs)
super(OptionalUniqueTogetherValidator, self).enforce_required_fields(attrs, serializer)
except ValidationError as e:
if set(e.detail.keys()) == set(self.fields):
raise SkipField()
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def get_package_data(package):

setup(
name='django-rest-framework-mongoengine',
version='3.4.0',
version='3.4.1',
description='MongoEngine support for Django Rest Framework.',
packages=get_packages('rest_framework_mongoengine'),
package_data=get_package_data('rest_framework_mongoengine'),
Expand Down
Loading

0 comments on commit 4d32ee1

Please sign in to comment.