Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ Gemfile.lock

/docs/.jekyll-metadata
/docs/djongocs/assets/*
*.map
*.maptmp/
2 changes: 1 addition & 1 deletion djongo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

__version__ = '1.3.7'
__version__ = '1.4.0'
14 changes: 14 additions & 0 deletions djongo/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,17 @@ class DatabaseFeatures(BaseDatabaseFeatures):
test_db_allows_multiple_connections = False
supports_unspecified_pk = True

# Django 3.1+ features
supports_json_field = True

# Django 4.0+ features
supports_expression_defaults = False
supports_table_check_constraints = False
supports_column_check_constraints = False
can_return_columns_from_insert = False
can_return_rows_from_bulk_insert = False

# Django 4.1+ features
supports_comments = False
supports_comments_inline = False

3 changes: 2 additions & 1 deletion djongo/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ def get_db_converters(self, expression):
converters.append(self.convert_datetimefield_value)
return converters

def sql_flush(self, style, tables, reset_sequences, allow_cascade=False):
def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False, **kwargs):
# TODO: Need to implement this fully
# Note: **kwargs added for Django 4.1+ compatibility (sequences parameter)
return [f'ALTER TABLE "{table}" FLUSH'
for table in tables]

Expand Down
15 changes: 8 additions & 7 deletions djongo/sql2mongo/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,10 @@ def __init__(self, *args):

def parse(self):
tok = self.statement.next()
if not tok.match(tokens.Keyword, 'BY'):
raise SQLDecodeError

tok = self.statement.next()
# In sqlparse >= 0.5, 'ORDER BY' is a single token, so there's no separate 'BY'
# In sqlparse < 0.5, 'ORDER' and 'BY' are separate tokens
if tok.match(tokens.Keyword, 'BY'):
tok = self.statement.next()
self.columns.extend(SQLToken.tokens2sql(tok, self.query))

def to_mongo(self):
Expand Down Expand Up @@ -443,9 +443,10 @@ def __init__(self, *args):

def parse(self):
tok = self.statement.next()
if not tok.match(tokens.Keyword, 'BY'):
raise SQLDecodeError
tok = self.statement.next()
# In sqlparse >= 0.5, 'GROUP BY' is a single token, so there's no separate 'BY'
# In sqlparse < 0.5, 'GROUP' and 'BY' are separate tokens
if tok.match(tokens.Keyword, 'BY'):
tok = self.statement.next()
self.sql_tokens.extend(SQLToken.tokens2sql(tok, self.query))

def to_mongo(self):
Expand Down
37 changes: 27 additions & 10 deletions djongo/sql2mongo/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
Where,
Statement)

# Try to import Values for sqlparse >= 0.4.0
try:
from sqlparse.sql import Values
except ImportError:
Values = None

from ..exceptions import SQLDecodeError, MigrationError, print_warn
from .functions import SQLFunc
from .sql_tokens import (SQLToken, SQLStatement, SQLIdentifier,
Expand Down Expand Up @@ -128,7 +134,8 @@ def parse(self):
elif tok.match(tokens.Keyword, 'LIMIT'):
self.limit = LimitConverter(self, statement)

elif tok.match(tokens.Keyword, 'ORDER'):
elif tok.match(tokens.Keyword, 'ORDER') or tok.match(tokens.Keyword, 'ORDER BY'):
# Handle both 'ORDER' (sqlparse < 0.5) and 'ORDER BY' (sqlparse >= 0.5)
self.order = OrderConverter(self, statement)

elif tok.match(tokens.Keyword, 'OFFSET'):
Expand All @@ -142,7 +149,8 @@ def parse(self):
converter = OuterJoinConverter(self, statement)
self.joins.append(converter)

elif tok.match(tokens.Keyword, 'GROUP'):
elif tok.match(tokens.Keyword, 'GROUP') or tok.match(tokens.Keyword, 'GROUP BY'):
# Handle both 'GROUP' (sqlparse < 0.5) and 'GROUP BY' (sqlparse >= 0.5)
self.groupby = GroupbyConverter(self, statement)

elif tok.match(tokens.Keyword, 'HAVING'):
Expand Down Expand Up @@ -356,17 +364,26 @@ def _columns(self, statement: SQLStatement):
def _fill_values(self, statement: SQLStatement):
for tok in statement:
if isinstance(tok, Parenthesis):
placeholder = SQLToken.token2sql(tok, self)
values = []
for index in placeholder:
if isinstance(index, int):
values.append(self.params[index])
else:
values.append(index)
self._values.append(values)
self._process_values_parenthesis(tok)
elif Values is not None and isinstance(tok, Values):
# sqlparse >= 0.4.0 wraps VALUES (...) in a Values token
for subtok in tok.tokens:
if isinstance(subtok, Parenthesis):
self._process_values_parenthesis(subtok)
elif not tok.match(tokens.Keyword, 'VALUES'):
raise SQLDecodeError

def _process_values_parenthesis(self, tok):
"""Process a Parenthesis token containing values."""
placeholder = SQLToken.token2sql(tok, self)
values = []
for index in placeholder:
if isinstance(index, int):
values.append(self.params[index])
else:
values.append(index)
self._values.append(values)

def execute(self):
docs = []
num = len(self._values)
Expand Down
4 changes: 2 additions & 2 deletions djongo/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ def _get_subcollections(collection):
"""
Returns all sub-collections of `collection`.
"""
# XXX: Use the MongoDB API for this once it exists.
for name in collection.database.collection_names():
# Use list_collection_names() which is available in PyMongo 3.7+
for name in collection.database.list_collection_names():
cleaned = name[:name.rfind('.')]
if cleaned != collection.name and cleaned.startswith(collection.name):
yield cleaned
Expand Down
28 changes: 21 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,32 @@ requires = ["setuptools"]
dynamic = ["version", "optional-dependencies"]
name = "djongo"
dependencies = [
'sqlparse==0.2.4',
'pymongo>=3.7.0,<=3.11.4',
'django>=2.1,<=3.1.12',
'sqlparse>=0.4.4',
'pymongo>=3.7.0',
'django>=2.1',
'pytz>=2018.5'
]
authors = [
{ name = "doableware", email = 'support@doableware.com' }
]
license= {text = "AGPL"}
keywords = ["Django", "Djongo", "MongoDB", "driver", "connector"]
requires-python = ">=3.6"
requires-python = ">=3.8"
classifiers = [
'Development Status :: 3 - Alpha',
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3.6',
'License :: OSI Approved :: GNU Affero General Public License v3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Framework :: Django :: 3.2',
'Framework :: Django :: 4.0',
'Framework :: Django :: 4.1',
'Framework :: Django :: 4.2',
'Framework :: Django :: 5.0',
'Framework :: Django :: 5.1',
]
description = "Djongo: The Django MongoDB connector"
readme = "README.md"
Expand All @@ -30,5 +40,9 @@ Homepage = "https://www.djongomapper.com/"
Documentation = "https://www.djongomapper.com/docs/"
Repository = "https://github.com/doableware/djongo.git"

[tool.setuptools.packages.find]
include = ["djongo*"]
exclude = ["tmp*", "docs*", "tests*"]

[tool.setuptools.dynamic]
version = {attr = "djongo.__version__"}