Skip to content

Commit

Permalink
Fixed id management and resolving missing models in contenttype resol…
Browse files Browse the repository at this point in the history
…ution
  • Loading branch information
Alberto Paro committed Apr 22, 2010
1 parent bb4cfb6 commit 95c9535
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 253 deletions.
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ TODO (ramdom order):
- geofield
- set field
- special manager for complex query (map/reduce)

- Master and Slave support
- more testing

Settings configuration
----------------------
Expand Down
2 changes: 1 addition & 1 deletion mongodj/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def table_names(self):
"""
Show defined models
"""
return self.django_table_names()
return self.connection.db_connection.collection_names()

def sequence_list(self):
# TODO: check if it's necessary to implement that
Expand Down
9 changes: 0 additions & 9 deletions mongodj/compiler.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import datetime
import sys

from testproj.myapp.models import StringAutoField

import pymongo
from pymongo.objectid import ObjectId

Expand Down Expand Up @@ -205,13 +203,6 @@ def execute_sql(self, return_id=False):
# every object should have a unique pk
pk_field = self.query.model._meta.pk
pk_name = pk_field.attname
if not dat.has_key(pk_name):
if isinstance(pk_field, StringAutoField):
dat[pk_name] = str(ObjectId())
# else:
# # create a random, hopefully unique 64 bits value
# import random
# dat[pk_name] = str(random.getrandbits(64))
if pk_name=='id' and pk_name in dat and type(pk_field).__name__ =="AutoField":
pk = dat.pop(pk_name)
if isinstance(pk, (str, unicode)):
Expand Down
62 changes: 61 additions & 1 deletion mongodj/creation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pymongo.collection import Collection
from django.db.models.fields import FieldDoesNotExist
from django.db.backends.creation import BaseDatabaseCreation

TEST_DATABASE_PREFIX = 'test_'
Expand Down Expand Up @@ -34,6 +36,64 @@ class DatabaseCreation(BaseDatabaseCreation):
}


def sql_indexes_for_field(self, model, f, style):
opts = model._meta
col = getattr(self.connection.db_connection, opts.db_table)
if f.db_index:
direction = (getattr(f, "index_descending") and -1) or 1
col.ensure_index([(f.name, direction)], unique=f.unique)
return []

def index_fields_group(self, model, group, style):
if not isinstance(group, dict):
raise TypeError, "Indexes group has to be instance of dict"

fields = group.pop("fields")

if not isinstance(fields, list):
raise TypeError, "index_together fields has to be instance of list"

opts = model._meta
col = getattr(self.connection.db_connection, opts.db_table)
checked_fields = []
model_fields = [ f.name for f in opts.local_fields]

for field in fields:
field_name = field
direction = 1
if isinstance(field, (tuple,list)):
field_name = field[0]
direction = (field[1] and 1) or -1
if not field_name in model_fields:
raise FieldDoesNotExist('%s has no field named %r' % (opts.object_name, field_name))
checked_fields.append((field_name, direction))
col.ensure_index(checked_fields, **group)

def sql_indexes_for_model(self, model, style):
"Returns the CREATE INDEX SQL statements for a single model"
if not model._meta.managed or model._meta.proxy:
return []
fields = [ f for f in model._meta.local_fields if f.db_index]
if not fields and not hasattr(model._meta, "index_together"):
return []
print "Installing index for %s.%s model" % (model._meta.app_label, model._meta.object_name)
for f in model._meta.local_fields:
self.sql_indexes_for_field(model, f, style)
for group in getattr(model._meta, "index_together", []):
self.index_fields_group(model, group, style)
return []

def sql_create_model(self, model, style, known_models=set()):
opts = model._meta
kwargs = {}
kwargs["capped"] = getattr(opts, "capped", False)
if hasattr(opts, "collection_max") and opts.collection_max:
kwargs["max"] = opts.collection_max
if hasattr(opts, "collection_size") and opts.collection_size:
kwargs["size"] = opts.collection_size
col = Collection(self.connection.db_connection.db, opts.db_table, **kwargs)
return [], {}

def set_autocommit(self):
"Make sure a connection is in autocommit mode."
pass
Expand Down Expand Up @@ -83,4 +143,4 @@ def destroy_test_db(self, old_database_name, verbosity=1):

def _drop_database(self, database_name):
self.connection.db_connection.conn.drop_database(database_name)


9 changes: 5 additions & 4 deletions mongodj/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ class EmbeddedModel(models.Model):

def save(self, *args, **kwargs):
if self.pk is None:
self.pk = ObjectId()
self.pk = unicode(ObjectId())
if self._embedded_in is None:
raise RuntimeError("Invalid save")
self._embedded_in.save()

def serialize(self):
if self.pk is None:
self.pk = ObjectId()
self.pk = unicode(ObjectId())
self.id = self.pk
result = {'_app':self._meta.app_label,
'_model':self._meta.module_name,
'_id':self.pk}
Expand Down Expand Up @@ -240,10 +241,10 @@ def fix_autofield(sender, **kwargs):
Fix autofield
"""
cls = sender
if isinstance(cls._meta.pk, DJAutoField):
if cls.objects.db =="mongodj" and isinstance(cls._meta.pk, DJAutoField):
pk = cls._meta.pk
setattr(pk, "to_python", autofield_to_python)
setattr(pk, "get_prep_value", autofield_get_prep_value)


signals.class_prepared.connect(fix_autofield)
signals.class_prepared.connect(fix_autofield)
24 changes: 22 additions & 2 deletions mongodj/mongodb_serializer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
from pymongo import Connection
from pymongo.son_manipulator import SONManipulator
from django.utils.importlib import import_module

#TODO Add content type cache

def encode_django(model):
from .fields import EmbeddedModel
if isinstance(model, EmbeddedModel):
res = model.serialize()
res["_type"] = "emb"
from django.contrib.contenttypes.models import ContentType
try:
ContentType.objects.get(app_label=res['_app'], model=res['_model'])
except:
res['_app'] = model.__class__.__module__
res['_model'] = model._meta.object_name

return res
if not model.pk:
model.save()
Expand All @@ -16,29 +26,39 @@ def encode_django(model):

def decode_django(data):
from django.contrib.contenttypes.models import ContentType
model = ContentType.objects.get(app_label=data['_app'], model=data['_model'])
if data['_type']=="django":
model = ContentType.objects.get(app_label=data['_app'], model=data['_model'])
return model.get_object_for_this_type(pk=data['pk'])
elif data['_type']=="emb":
try:
model = ContentType.objects.get(app_label=data['_app'], model=data['_model']).model_class()
except:
module = import_module(data['_app'])
model = getattr(module, data['_model'])

del data['_type']
del data['_app']
del data['_model']
data.pop('_id', None)
data = dict([(str(k),v) for k,v in data.items()])
return model.model_class()(**data)
return model(**data)

class TransformDjango(SONManipulator):
def transform_incoming(self, son, collection):
from django.db.models import Model
from .fields import EmbeddedModel
if isinstance(son, dict):
for (key, value) in son.items():
if isinstance(value, (str, unicode)):
continue
if isinstance(value, (Model, EmbeddedModel)):
son[key] = encode_django(value)
elif isinstance(value, dict): # Make sure we recurse into sub-docs
son[key] = self.transform_incoming(value, collection)
elif hasattr(value, "__iter__"): # Make sure we recurse into sub-docs
son[key] = [self.transform_incoming(item, collection) for item in value]
elif isinstance(son, (str, unicode)):
pass
elif hasattr(son, "__iter__"): # Make sure we recurse into sub-docs
son = [self.transform_incoming(item, collection) for item in son]
elif isinstance(son, (Model, EmbeddedModel)):
Expand Down
5 changes: 4 additions & 1 deletion mongodj/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ def __init__(self):
from django.conf import settings
self.managed_apps = [app.split('.')[-1] for app in getattr(settings, "MONGODB_MANAGED_APPS", [])]
self.mongodb_database = None
self.mongodb_databases = []
for name, databaseopt in settings.DATABASES.items():
if databaseopt["ENGINE"]=='mongodj':
self.mongodb_database = name
self.mongodb_databases.append(name)
if self.mongodb_database is None:
raise RuntimeError("A mongodb database must be set")

Expand All @@ -32,9 +34,10 @@ def allow_relation(self, obj1, obj2, **hints):

def allow_syncdb(self, db, model):
"Make sure that a mongodb model appears on a mongodb database"
if db == self.mongodb_database:
if db in self.mongodb_databases:
return model._meta.app_label in self.managed_apps
elif model._meta.app_label in self.managed_apps:
return False
return None


6 changes: 5 additions & 1 deletion testproj/myapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def db_type(self, connection):
class Blog(models.Model):
# _id = StringAutoField(max_length=100, primary_key=True)
title = models.CharField(max_length=200)

class Meta:
capped = True
collection_max = 25

def __unicode__(self):
return "Blog: %s" % self.title
Expand Down Expand Up @@ -97,4 +101,4 @@ class TestFieldModel(models.Model):
mset_default = SetListField(default=set(["a", 'b']))

def __unicode__(self):
return "Test special field model: %s" % (self.title)
return "Test special field model: %s" % (self.title)
Loading

0 comments on commit 95c9535

Please sign in to comment.