Skip to content

Commit 9d56f6a

Browse files
refactor: Switch to mongoengine style field arguments. Drop old django (#200)
BREAKING CHANGE: Removed `blank` argument support for fields, use mongoengine `required` instead.
1 parent 1a08b5a commit 9d56f6a

File tree

11 files changed

+138
-118
lines changed

11 files changed

+138
-118
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ jobs:
1313
strategy:
1414
matrix:
1515
python:
16-
- "3.8"
1716
- "3.9"
1817
- "3.10"
1918
- "3.11"
19+
- "3.12"
2020
- "pypy3.9"
2121
- "pypy3.10"
2222
django:
23-
- "Django>=3.2,<3.3"
2423
- "Django>=4.2,<4.3"
2524
env:
2625
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Right now we're targeting to get things working on Django 4.2;
2424
WARNING: This project is not in good state, and is likely to break with django updates.
2525
It's better to use raw mongoengine.
2626

27-
Working / Django 3.2-4.2
27+
Working / Django 4.2
2828
------------------------
2929

3030
* [ok] sessions
@@ -50,9 +50,9 @@ It get's replaced after class creation via some metaclass magick.
5050
Fields notes
5151
------------
5252

53-
* mongo defaults Field(required=False), changed to django-style defaults
54-
-> Field(blank=False), and setting required = not blank in Field.__init__
55-
53+
* Project uses mongoengine style argument `required=False`, not django style `blank=False`,
54+
to be compatible with mongo-types.
55+
**All your fields are optional by default.**
5656

5757

5858
TODO

django_mongoengine/document.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from __future__ import annotations
33

44
from functools import partial
5-
from typing import TYPE_CHECKING
5+
from typing import TYPE_CHECKING, Any
66

77
from bson.objectid import ObjectId
88
from django.db.models import Model
@@ -23,7 +23,7 @@
2323
# TopLevelDocumentMetaclass is using ObjectIdField to create default pk field,
2424
# if one's not set explicitly.
2525
# We need to know it's not editable and auto_created.
26-
mtc.ObjectIdField = partial(ObjectIdField, editable=False, auto_created=True, blank=True)
26+
mtc.ObjectIdField = partial(ObjectIdField, editable=False, auto_created=True)
2727

2828

2929
def django_meta(meta, *top_bases):
@@ -51,7 +51,7 @@ class DjangoFlavor:
5151
objects = QuerySetManager["Self"]()
5252
_default_manager = QuerySetManager["Self"]()
5353
_get_pk_val = Model.__dict__["_get_pk_val"]
54-
_meta: DocumentMetaWrapper
54+
_meta: DocumentMetaWrapper | dict[str, Any]
5555
DoesNotExist: type[DoesNotExist]
5656

5757
def __init__(self, *args, **kwargs):

django_mongoengine/fields/djangoflavor.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010

1111
from .internal import INTERNAL_DJANGO_FIELDS_MAP
1212

13-
_field_defaults = (
14-
("blank", False),
15-
("null", False),
16-
("help_text", ""),
17-
("editable", True),
18-
("auto_created", False),
19-
)
13+
# Add some default values, required for django.
14+
_field_defaults = {
15+
"help_text": "",
16+
"editable": True,
17+
"auto_created": False,
18+
}
2019

2120

2221
class DjangoField:
@@ -37,13 +36,13 @@ def _get_flatchoices(self):
3736
flatchoices = property(_get_flatchoices)
3837

3938
def __init__(self, *args, **kwargs):
40-
for k, v in _field_defaults:
41-
kwargs.setdefault(k, v)
42-
if "required" in kwargs:
39+
kwargs = _field_defaults | kwargs
40+
41+
if "blank" in kwargs:
4342
raise ImproperlyConfigured(
44-
"`required` option is not supported. Use Django-style `blank` instead."
43+
"`blank` option is not supported. Use Mongoengine-style `required` instead."
4544
)
46-
kwargs["required"] = not kwargs["blank"]
45+
4746
if hasattr(self, "auto_created"):
4847
kwargs.pop("auto_created")
4948
self._verbose_name = kwargs.pop("verbose_name", None)
@@ -53,6 +52,9 @@ def __init__(self, *args, **kwargs):
5352

5453
self.remote_field = None
5554
self.is_relation = self.remote_field is not None
55+
# This is needed for django, but we use mongoengine style in __init__
56+
# to not confuse type checker.
57+
self.blank = not self.required
5658

5759
def _get_verbose_name(self):
5860
return self._verbose_name or self.db_field.replace('_', ' ')
@@ -272,7 +274,7 @@ def formfield(self, **kwargs):
272274
elif isinstance(self.field, fields.ReferenceField):
273275
defaults = {
274276
'form_class': formfields.DocumentMultipleChoiceField,
275-
'queryset': self.field.document_type.objects,
277+
'queryset': self.field.document_type.objects, # type: ignore
276278
}
277279
else:
278280
defaults = {}

django_mongoengine/mongo_auth/models.py

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
from django.contrib.contenttypes.models import ContentTypeManager
1111
from django.db import models
1212
from django.utils import timezone
13-
from django.utils.encoding import smart_str
1413
from django.utils.translation import gettext_lazy as _
1514
from mongoengine import ImproperlyConfigured
1615

1716
from django_mongoengine import document, fields
1817
from django_mongoengine.queryset import QuerySetManager
1918

19+
from django.contrib.auth.hashers import check_password, make_password
2020
from .managers import MongoUserManager
2121

2222

@@ -34,32 +34,6 @@ def ct_init(self, *args, **kwargs):
3434
),
3535
)
3636

37-
try:
38-
from django.contrib.auth.hashers import check_password, make_password
39-
except ImportError:
40-
"""Handle older versions of Django"""
41-
from django.utils.hashcompat import md5_constructor, sha_constructor
42-
43-
def get_hexdigest(algorithm, salt, raw_password):
44-
raw_password, salt = smart_str(raw_password), smart_str(salt)
45-
if algorithm == 'md5':
46-
return md5_constructor(salt + raw_password).hexdigest()
47-
elif algorithm == 'sha1':
48-
return sha_constructor(salt + raw_password).hexdigest()
49-
raise ValueError('Got unknown password algorithm type in password')
50-
51-
def check_password(raw_password, password):
52-
algo, salt, hash = password.split('$')
53-
return hash == get_hexdigest(algo, salt, raw_password)
54-
55-
def make_password(raw_password):
56-
from random import random
57-
58-
algo = 'sha1'
59-
salt = get_hexdigest(algo, str(random()), str(random()))[:5]
60-
hash = get_hexdigest(algo, salt, raw_password)
61-
return '%s$%s$%s' % (algo, salt, hash)
62-
6337

6438
class BaseUser:
6539
is_anonymous = AbstractBaseUser.__dict__['is_anonymous']
@@ -88,7 +62,7 @@ class Meta:
8862
# ordering = ('name',)
8963
# unique_together = (('app_label', 'model'),)
9064

91-
def __unicode__(self):
65+
def __str__(self):
9266
return self.name
9367

9468
def model_class(self):
@@ -158,7 +132,7 @@ class Meta:
158132
# unique_together = (('content_type', 'codename'),)
159133
# ordering = ('content_type__app_label', 'content_type__model', 'codename')
160134

161-
def __unicode__(self):
135+
def __str__(self):
162136
return "%s | %s | %s" % (
163137
self.content_type.app_label,
164138
self.content_type,
@@ -195,7 +169,7 @@ class Meta:
195169
verbose_name = _('group')
196170
verbose_name_plural = _('groups')
197171

198-
def __unicode__(self):
172+
def __str__(self):
199173
return self.name
200174

201175

@@ -208,22 +182,23 @@ class AbstractUser(BaseUser, document.Document):
208182
max_length=150,
209183
verbose_name=_('username'),
210184
help_text=_("Required. 150 characters or fewer. Letters, numbers and @/./+/-/_ characters"),
185+
required=True,
211186
)
212187

213188
first_name = fields.StringField(
214189
max_length=30,
215-
blank=True,
216190
verbose_name=_('first name'),
217191
)
218192

219-
last_name = fields.StringField(max_length=30, blank=True, verbose_name=_('last name'))
220-
email = fields.EmailField(verbose_name=_('e-mail address'), blank=True)
193+
last_name = fields.StringField(max_length=30, verbose_name=_('last name'))
194+
email = fields.EmailField(verbose_name=_('e-mail address'), required=True)
221195
password = fields.StringField(
222196
max_length=128,
223197
verbose_name=_('password'),
224198
help_text=_(
225199
"Use '[algo]$[iterations]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."
226200
),
201+
required=True,
227202
)
228203
is_staff = fields.BooleanField(
229204
default=False,
@@ -250,7 +225,6 @@ class AbstractUser(BaseUser, document.Document):
250225
user_permissions = fields.ListField(
251226
fields.ReferenceField(Permission),
252227
verbose_name=_('user permissions'),
253-
blank=True,
254228
help_text=_('Permissions for the user.'),
255229
)
256230

@@ -259,7 +233,7 @@ class AbstractUser(BaseUser, document.Document):
259233

260234
meta = {'abstract': True, 'indexes': [{'fields': ['username'], 'unique': True, 'sparse': True}]}
261235

262-
def __unicode__(self):
236+
def __str__(self):
263237
return self.username
264238

265239
def get_full_name(self):
Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
try:
2-
from django.urls import reverse
3-
except ImportError:
4-
from django.core.urlresolvers import reverse
1+
from django.urls import reverse
52

63
import datetime
74

@@ -13,58 +10,61 @@ class Comment(EmbeddedDocument):
1310
default=datetime.datetime.now,
1411
editable=False,
1512
)
16-
author = fields.StringField(verbose_name="Name", max_length=255)
17-
email = fields.EmailField(verbose_name="Email", blank=True)
18-
body = fields.StringField(verbose_name="Comment")
13+
author = fields.StringField(verbose_name="Name", max_length=255, required=True)
14+
email = fields.EmailField(verbose_name="Email", required=True)
15+
body = fields.StringField(verbose_name="Comment", required=True)
1916

2017

2118
class Post(Document):
2219
created_at = fields.DateTimeField(
2320
default=datetime.datetime.now,
2421
editable=False,
2522
)
26-
title = fields.StringField(max_length=255)
27-
slug = fields.StringField(max_length=255, primary_key=True)
23+
title = fields.StringField(max_length=255, required=True)
24+
slug = fields.StringField(max_length=255, primary_key=True, required=True)
2825
comments = fields.ListField(
29-
fields.EmbeddedDocumentField('Comment'),
26+
fields.EmbeddedDocumentField(Comment, required=True),
27+
default=[],
28+
)
29+
strings = fields.ListField(
30+
fields.StringField(required=True),
3031
default=[],
31-
blank=True,
3232
)
3333

3434
def get_absolute_url(self):
35-
return reverse('post', kwargs={"slug": self.slug})
35+
return reverse("post", kwargs={"slug": self.slug})
3636

37-
def __unicode__(self):
37+
def __str__(self):
3838
return self.title
3939

4040
@property
4141
def post_type(self):
4242
return self.__class__.__name__
4343

4444
meta = {
45-
'indexes': ['-created_at', 'slug'],
46-
'ordering': ['-created_at'],
47-
'allow_inheritance': True,
45+
"indexes": ["-created_at", "slug"],
46+
"ordering": ["-created_at"],
47+
"allow_inheritance": True,
4848
}
4949

5050

5151
class BlogPost(Post):
52-
body = fields.StringField()
52+
body = fields.StringField(required=True)
5353

5454

5555
class Video(Post):
56-
embed_code = fields.StringField()
56+
embed_code = fields.StringField(required=True)
5757

5858

5959
class Image(Post):
60-
image = fields.ImageField()
60+
image = fields.ImageField(required=True)
6161

6262

6363
class Quote(Post):
64-
body = fields.StringField()
65-
author = fields.StringField(verbose_name="Author Name", max_length=255)
64+
body = fields.StringField(required=True)
65+
author = fields.StringField(verbose_name="Author Name", max_length=255, required=True)
6666

6767

6868
class Music(Post):
69-
url = fields.StringField(max_length=100, verbose_name="Music Url")
70-
music_parameters = fields.DictField(verbose_name="Music Parameters")
69+
url = fields.StringField(max_length=100, verbose_name="Music Url", required=True)
70+
music_parameters = fields.DictField(verbose_name="Music Parameters", required=True)

0 commit comments

Comments
 (0)