Skip to content

Commit 5e63e05

Browse files
committed
Merge pull request #12 from idlead/master
Restructuring and EnumField
2 parents 9cf13ca + 141dfd1 commit 5e63e05

File tree

10 files changed

+268
-148
lines changed

10 files changed

+268
-148
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.pyc
2+
build/
23
venv/

extras_mongoengine/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import fields
2+
3+
__all__ = ('fields')

extras_mongoengine/django_fields.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import datetime
3+
4+
from mongoengine.base import BaseField
5+
from mongoengine.python_support import str_types
6+
7+
from django.db.models.fields.files import FieldFile
8+
from django.core.files.base import File
9+
from django.core.files.storage import default_storage
10+
11+
from django.utils.encoding import force_str, force_text
12+
13+
14+
class LocalStorageFileField(BaseField):
15+
16+
proxy_class = FieldFile
17+
18+
def __init__(self,
19+
size=None,
20+
name=None,
21+
upload_to='',
22+
storage=None,
23+
**kwargs):
24+
self.size = size
25+
self.storage = storage or default_storage
26+
self.upload_to = upload_to
27+
if callable(upload_to):
28+
self.generate_filename = upload_to
29+
super(LocalStorageFileField, self).__init__(**kwargs)
30+
31+
def __get__(self, instance, owner):
32+
if instance is None:
33+
return self
34+
35+
file = instance._data.get(self.name)
36+
37+
if isinstance(file, str_types) or file is None:
38+
attr = self.proxy_class(instance, self, file)
39+
instance._data[self.name] = attr
40+
41+
return instance._data[self.name]
42+
43+
def __set__(self, instance, value):
44+
key = self.name
45+
if isinstance(value, File) and not isinstance(value, FieldFile):
46+
file = instance._data.get(self.name)
47+
if file:
48+
try:
49+
file.delete()
50+
except:
51+
pass
52+
# Create a new proxy object as we don't already have one
53+
file_copy = self.proxy_class(instance, self, value.name)
54+
file_copy.file = value
55+
instance._data[key] = file_copy
56+
else:
57+
instance._data[key] = value
58+
59+
instance._mark_as_changed(key)
60+
61+
def get_directory_name(self):
62+
return os.path.normpath(force_text(
63+
datetime.datetime.now().strftime(force_str(self.upload_to))))
64+
65+
def get_filename(self, filename):
66+
return os.path.normpath(
67+
self.storage.get_valid_name(os.path.basename(filename)))
68+
69+
def generate_filename(self, instance, filename):
70+
return os.path.join(
71+
self.get_directory_name(), self.get_filename(filename))
72+
73+
def to_mongo(self, value):
74+
if isinstance(value, self.proxy_class):
75+
return value.name
76+
return value

extras_mongoengine/fields.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from datetime import timedelta
2+
from mongoengine.base import BaseField
3+
from mongoengine.fields import IntField, StringField, EmailField
4+
5+
6+
class TimedeltaField(BaseField):
7+
"""A timedelta field.
8+
9+
Looks to the outside world like a datatime.timedelta, but stores
10+
in the database as an integer (or float) number of seconds.
11+
12+
"""
13+
def validate(self, value):
14+
if not isinstance(value, (timedelta, int, float)):
15+
self.error(u'cannot parse timedelta "%r"' % value)
16+
17+
def to_mongo(self, value):
18+
return self.prepare_query_value(None, value)
19+
20+
def to_python(self, value):
21+
return timedelta(seconds=value)
22+
23+
def prepare_query_value(self, op, value):
24+
if value is None:
25+
return value
26+
if isinstance(value, timedelta):
27+
return self.total_seconds(value)
28+
if isinstance(value, (int, float)):
29+
return value
30+
31+
@staticmethod
32+
def total_seconds(value):
33+
"""Implements Python 2.7's datetime.timedelta.total_seconds()
34+
for backwards compatibility with Python 2.5 and 2.6.
35+
36+
"""
37+
try:
38+
return value.total_seconds()
39+
except AttributeError:
40+
return (value.days * 24 * 3600) + \
41+
(value.seconds) + \
42+
(value.microseconds / 1000000.0)
43+
44+
45+
class LowerStringField(StringField):
46+
def __set__(self, instance, value):
47+
value = self.to_python(value)
48+
return super(LowerStringField, self).__set__(instance, value)
49+
50+
def to_python(self, value):
51+
if value:
52+
value = value.lower()
53+
return value
54+
55+
def prepare_query_value(self, op, value):
56+
value = value.lower() if value else value
57+
return super(LowerStringField, self).prepare_query_value(op, value)
58+
59+
60+
class LowerEmailField(LowerStringField):
61+
62+
def validate(self, value):
63+
if not EmailField.EMAIL_REGEX.match(value):
64+
self.error('Invalid Mail-address: %s' % value)
65+
super(LowerEmailField, self).validate(value)
66+
67+
68+
class EnumField(object):
69+
"""
70+
A class to register Enum type (from the package enum34) into mongo
71+
72+
:param choices: must be of :class:`enum.Enum`: type
73+
and will be used as possible choices
74+
"""
75+
76+
def __init__(self, enum, *args, **kwargs):
77+
self.enum = enum
78+
kwargs['choices'] = [choice for choice in enum]
79+
super(EnumField, self).__init__(*args, **kwargs)
80+
81+
def __get_value(self, enum):
82+
return enum.value if hasattr(enum, 'value') else enum
83+
84+
def to_python(self, value):
85+
return self.enum(super(EnumField, self).to_python(value))
86+
87+
def to_mongo(self, value):
88+
return self.__get_value(value)
89+
90+
def prepare_query_value(self, op, value):
91+
return super(EnumField, self).prepare_query_value(
92+
op, self.__get_value(value))
93+
94+
def validate(self, value):
95+
return super(EnumField, self).validate(self.__get_value(value))
96+
97+
def _validate(self, value, **kwargs):
98+
return super(EnumField, self)._validate(
99+
self.enum(self.__get_value(value)), **kwargs)
100+
101+
102+
class IntEnumField(EnumField, IntField):
103+
"""A variation on :class:`EnumField` for only int containing enumeration.
104+
"""
105+
pass
106+
107+
108+
class StringEnumField(EnumField, StringField):
109+
"""A variation on :class:`EnumField` for only string containing enumeration.
110+
"""
111+
pass

fields/fields.py

Lines changed: 0 additions & 139 deletions
This file was deleted.

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
unittest2
2-
django
32
mongoengine

setup.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env python
2+
from distutils.core import setup
3+
4+
setup(name='extras_mongoengine',
5+
version='0.1',
6+
description='MongoEngine Extras - Field Types and any other wizardry.',
7+
url='https://github.com/MongoEngine/extras-mongoengine/',
8+
install_requires=['mongoengine>=0.8.6'],
9+
packages=['extras_mongoengine'],
10+
)

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)