Kirill Mavreshko avatar Kirill Mavreshko committed b470dda

django admin, query logging, better primary keys handling and many fixes
* django admin work with simple models (without ForeignKey or ManyToManyField)
* support for query debugging via django.db.connection.queries
* better handling mongodb.ObjectId in auto primary keys

Comments (0)

Files changed (14)

 *.pyc
 local_settings.py
 django
-media/captcha/*.png
-media/photos/*
-openidstore/*
 *~
-captcha
-marktools
-cache/*
-openid/*
-media/orgs/*
-media/rieltdb/*
-media/vip/photos/*
-xapianindex/*
-media/upload/admaster/*
-admaster
-djart
-freespeach
+How to install django-mongodb
+=============================
+
+1. Make sure you have Python >= 2.4 and Django >= 1.0 installed.
+2. Install MongoDB from http://www.mongodb.org
+3. Download and install pymongo (Python <--> MongoDB driver) 
+   from http://github.com/mongodb/mongo-python-driver/tree/master
+4. Copy python_mongodb into your project directory.
+5. Change options in setting.py to similar:
+   
+   DATABASE_ENGINE = 'django_mongodb'
+   DATABASE_NAME = 'my_database_name'
+   DATABASE_USER = ''  # MongoDB authentication not supported yet
+   DATABASE_PASSWORD = ''
+   DATABASE_HOST = 'localhost'
+   DATABASE_PORT = '27017' # Default MongoDB port
+Copyright (c) 2009, Kirill Mavreshko (kimavr@gmail.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The name of the author may not be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.
+django-mongodb is a simple MongoDB (http://www.mongodb.org) database backend 
+for Django web framework (http://www.djangoproject.com/). 
+
+Unfortunately, Django ORM is strongly connected with RDBMS, and django-mongodb
+cannot support all it's features. But tries to do it.

django_mongodb/base.py

 django_mongodb.base -- Django to MongoDB (http://www.mongodb.org) database backend.
 """
 
+import sys, time
+
 from django.core.exceptions import ImproperlyConfigured
 from django.db.backends import *
-from django.db.backends.creation import BaseDatabaseCreation
+from django.db.backends.creation import BaseDatabaseCreation, TEST_DATABASE_PREFIX
 
 def complain(*args, **kwargs):
     raise ImproperlyConfigured("django_mongodb is not supporting all Django ORM abilities.")
     def value_to_db_datetime(self, value):
         return value
 
+    def sql_flush(self, *args, **kwargs):
+        return []
+
+
+class MongoDatabaseCreation(BaseDatabaseCreation):
+    sql_for_inline_foreign_key_references = ignore
+    sql_for_many_to_many = ignore
+    sql_for_many_to_many_field = ignore
+    sql_for_inline_many_to_many_references = ignore
+    sql_remove_table_constraints = ignore
+    sql_destroy_many_to_many = ignore
+    
+
+    sql_indexes_for_model = ignore
+    sql_indexes_for_field = ignore
+    sql_destroy_model = ignore
+
+    _rollback_works = lambda self: False
+    
+    def _create_test_db(self, verbosity, autoclobber):
+        "See django.db.backends.creation.BaseDatabaseCreation._create_test_db"
+        from django.conf import settings
+        suffix = self.sql_table_creation_suffix()
+
+        if settings.TEST_DATABASE_NAME:
+            test_database_name = settings.TEST_DATABASE_NAME
+        else:
+            test_database_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
+
+        mongodb_connection = self.connection.connection
+        
+        if test_database_name in mongodb_connection.database_names():
+            sys.stderr.write("Test database already exists: %s\n" % test_database_name)
+            if not autoclobber:
+                confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
+            if autoclobber or confirm == 'yes':
+                try:
+                    if verbosity >= 1:
+                        print "Destroying old test database..."
+                    mongodb_connection.drop_database(test_database_name)
+                    if verbosity >= 1:
+                        print "Creating test database..."
+                    mongodb_connection['test']
+                except Exception, e:
+                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
+                    sys.exit(2)
+            else:
+                print "Tests cancelled."
+                sys.exit(1)
+
+        return test_database_name
+
+
+    def _destroy_test_db(self, test_database_name, verbosity):
+        "See django.db.backends.creation.BaseDatabaseCreation._destroy_test_db"
+        mongodb_connection = self.connection.connection
+        time.sleep(1)
+        mongodb_connection.drop_database(test_database_name)
+        self.connection.close()
+
+    def sql_create_model(self, *args, **kwargs): 
+        return [],{}
+
+    def sql_for_pending_references(self, *args, **kwargs):
+        return []
+
+
+class MongoCollectionDebugWrapper(object):
+    """
+    Wrapper around pymongo.collection.Collection for Django debug mode query logging.
+    Used automatically in Django debug mode (DEBUG=True).
+    """
+
+    MONGO_LOGGED_CALLS = ("find", "remove", "update", "save")
+
+    def __init__(self, database, queries):
+        self._wrapped_mongo_collection = database
+        self._debug_queries_list = queries
+
+    def _mongo_request_args_to_sql(self, method, *args, **kwargs):
+        "Format call of method and append result message to django.db.connection.queries"
+        formatted_args = u", ".join([repr(a) for a in args])
+        formatted_kwargs = u", ".join([u"%s=%s" % (k,repr(v)) for k,v in kwargs.items()])
+        col_name = self._wrapped_mongo_collection.name()
+        return u"db.%s.%s(%r)" % (col_name, method, u", ".join([formatted_args, formatted_kwargs]))
+
+    def _call_logger(self, func):
+        "Logging decorator for all calls of MONGO_LOGGED_CALLS methods."
+        def logger(*args, **kwargs):
+            start = time.time()
+            try:
+                return func(*args, **kwargs)
+            finally:
+                stop = time.time()
+                sql = self._mongo_request_args_to_sql(func.func_name, *args, **kwargs)
+                self._debug_queries_list.append({
+                        'sql': sql,
+                        'time': "%.6f" % (stop - start),
+                        })
+                #print self._debug_queries_list
+        return logger
+
+    def __getattr__(self, attr):
+        if attr in self.__dict__:
+            return self.__dict__[attr]
+        else:
+            rval = getattr(self._wrapped_mongo_collection, attr)
+            if attr in self.MONGO_LOGGED_CALLS:
+                rval = self._call_logger(rval)
+            return rval
+
+
+class MongoDatabaseDebugWrapper(object):
+    """
+    Wrapper around pymongo.collection.Database for Django debug mode query logging.
+    Used automatically in Django debug mode (DEBUG=True).
+    """
+
+    MONGO_LOGGED_CALLS = ("find", "remove", "update", "save")
+
+    def __init__(self, database, queries):
+        self._wrapped_mongo_database = database
+        self._debug_queries_list = queries
+
+    def __getattr__(self, attr):
+        from pymongo.collection import Collection
+        rval = getattr(self._wrapped_mongo_database, attr)
+        if isinstance(rval, Collection):
+            rval = MongoCollectionDebugWrapper(rval, self._debug_queries_list)
+        return rval
+
+    def __getitem__(self, name):
+        return self.__getattr__(name)
+
+
 class DatabaseClient(BaseDatabaseClient):
     runshell = complain
 
-class DatabaseIntrospection(BaseDatabaseIntrospection):
+
+class MongoDatabaseIntrospection(BaseDatabaseIntrospection):
     get_table_list = complain
     get_table_description = complain
     get_relations = complain
     get_indexes = complain
 
+    def table_names(self):
+        return self.connection.cursor().collection_names()
+
+
 class MongoDatabaseFeatures(BaseDatabaseFeatures):
     uses_custom_query_class = True
 
     # integer primary keys.
     related_fields_match_type = False
 
-class DatabaseWrapper(object):
-    cursor = complain
+
+class DatabaseWrapper(BaseDatabaseWrapper):
     _commit = ignore
     _rollback = ignore
-    _enter_transaction_management = ignore
-    _leave_transaction_management = ignore
-    _savepoint = ignore
-    _savepoint_commit = ignore
-
     operators = {}
 
     def __init__(self, settings_dict):
         self.features = MongoDatabaseFeatures()
         self.ops = MongoDatabaseOperations()
         self.client = DatabaseClient(self)
-        self.creation = BaseDatabaseCreation(self)
-        self.introspection = DatabaseIntrospection(self)
+        self.creation = MongoDatabaseCreation(self)
+        self.introspection = MongoDatabaseIntrospection(self)
         self.validation = BaseDatabaseValidation()
 
-        self.connection = None
+        self._connection = None
         self.queries = []
         self.settings_dict = settings_dict
 
     def close(self):
-        if self.connection is not None:
-            del self.connection
-            self.connection = None
+        if self._connection is not None:
+            del self._connection
+            self._connection = None
 
-    def cursor(self):
-        from pymongo.connection import Connection
-        if self.connection is None:
-            self.connection = Connection(self.settings_dict['DATABASE_HOST'], int(self.settings_dict['DATABASE_PORT']))
+    def make_debug_cursor(self, cursor):
+        return MongoDatabaseDebugWrapper(cursor, self.queries)
+
+    def _cursor(self):
         return getattr(self.connection, self.settings_dict['DATABASE_NAME'])
+
+    @property
+    def connection(self):
+        if self._connection is None:
+            from pymongo.connection import Connection
+            self._connection = Connection(self.settings_dict['DATABASE_HOST'], int(self.settings_dict['DATABASE_PORT']))
+        return self._connection
+    

django_mongodb/djangopatch.py

 #-*- coding:utf-8 -*-
 
-# This file is part of django_mongodb.
+# Copyright (c) 2009, Kirill Mavreshko (kimavr@gmail.com)
+# All rights reserved.
 
-# django_mongodb is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# Redistribution and use in source and binary forms, with or without 
+# modification, are permitted provided that the following conditions are met:
 
-# django_mongodb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Lesser General Public License for more details.
+#    * Redistributions of source code must retain the above copyright notice, 
+#      this list of conditions and the following disclaimer.
+#    * Redistributions in binary form must reproduce the above copyright notice,
+#      this list of conditions and the following disclaimer in the documentation
+#      and/or other materials provided with the distribution.
+#    * The name of the author may not be used to endorse or promote products
+#      derived from this software without specific prior written permission.
 
-# You should have received a copy of the GNU Lesser General Public License
-# along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+# POSSIBILITY OF SUCH DAMAGE.
 
 """
 django_mongodb.djangopatch -- Collection of MongoDB-related monkey patches for Django ORM.
 """
 
+from pymongo.objectid import ObjectId
+
+_django_is_patched = False
+
+INVALID_DJANGO_COMMANDS = ("sqlclear","sqlall",)
+
+
+def AutoField_to_python(self, value):
+    return value
+
+def AutoField_get_db_prep_value(self, value):
+    return value
+
+def apply_mongo_patch():
+    global _django_is_patched
+    if not _django_is_patched:
+        from django.db.models import fields
+        from django.db.models.sql import subqueries
+        from django_mongodb import query
+        from django.core import management
+        fields.AutoField.to_python = AutoField_to_python
+        fields.AutoField.get_db_prep_value = AutoField_get_db_prep_value
+        
+        subqueries.UpdateQuery.execute_sql = query.UpdateQuery_execute_sql
+        subqueries.DeleteQuery.execute_sql = query.DeleteQuery_execute_sql
+        subqueries.InsertQuery.execute_sql = query.InsertQuery_execute_sql
+
+        django_commands = management.get_commands()
+        for cmd in INVALID_DJANGO_COMMANDS:
+            del django_commands[cmd]
+
+        _django_is_patched = True

django_mongodb/query.py

 django_mongodb.query -- Core of django_mongodb query engine
 """
 
-import re
+import re, sys
 from copy import deepcopy
 
 from pymongo.objectid import ObjectId
 from pymongo import ASCENDING, DESCENDING
 
+from django.db import DatabaseError
 from django.db.models.sql.query import BaseQuery
 from django.db.models.sql.constants import *
 from django.db.models.sql.datastructures import Empty, EmptyResultSet
 from django.db.models.sql.where import AND, OR
 from django.utils.datastructures import SortedDict
 
-
 class MongoDbQuery(BaseQuery):
     """
     Alternative of django.db.models.query.sql.BaseQuery for MongoDB
     """
 
     def __init__(self, *args, **kwargs):
-        apply_mongo_patch()
+        from django_mongodb import djangopatch
+        djangopatch.apply_mongo_patch()
         super(MongoDbQuery, self).__init__(*args, **kwargs)
 
     def where_to_mongo_query(self, node, parent_negated=False):
         Convert django.db.models.sql.where.WhereNode tree 
         to query dict for MongoDB "find" collection method.
         """
-        from django.db import DatabaseError
+        from django.db import models
+        pk_field = self.model._meta.pk
         if isinstance(node, tuple):
             lvalue, lookup_type, value_annot, params = node
             field_name = lvalue[1]
-            if field_name == self.model._meta.pk.name:
+            if field_name == pk_field.name:
                 field_name = "_id"
+                if isinstance(pk_field, models.AutoField):
+                    params = [isinstance(par, ObjectId) and par or ObjectId.url_decode(par) for par in params]
             if lookup_type == "exact":
                 if parent_negated:
                     res = {field_name: {"$ne": params[0]}}
         """
         rval = []
         for col in out_cols:
-            #(u'1', [])
             orig_field = cols_map[col]
             if isinstance(orig_field, tuple): # extra select
                 extra_expression, extra_params = orig_field
 
 def InsertQuery_execute_sql(self, return_id=False):
     "see django.db.models.sql.subqueries.InsertQuery.execute_sql method"
+    from django.db import models
     self.return_id = return_id
-    
     db = self.connection.cursor()
     collection = getattr(db, self.model._meta.db_table)
-    pk_name = self.model._meta.pk.name
+    pk_field = self.model._meta.pk
+    pk_name = pk_field.name
     cols = [c == pk_name and "_id" or c for c in self.columns]
     doc = dict(zip(cols, self.params))
+    if not (isinstance(pk_field, models.AutoField) or doc.get("_id")):
+        raise DatabaseError("You must provide value for %r primary key" % pk_name)
     rval = collection.save(doc)
-    return unicode(rval.url_encode())
+    if isinstance(rval, ObjectId):
+        rval = rval.url_encode()
+    return rval
 
 def DeleteQuery_execute_sql(self, *args, **kwargs):
     "see django.db.models.sql.subqueries.DeleteQuery.execute_sql method"
     query = self.where_to_mongo_query(self.where)
     collection.remove(query)
 
-def AutoField_to_python(self, value):
-    if isinstance(value, ObjectId):
-        return value
-    return value
-
-def AutoField_get_db_prep_value(self, value):
-    if isinstance(value, basestring):
-        value = ObjectId.url_decode(value)
-    return value
-
-
-_patch_info = {'applied':False}
-
-def apply_mongo_patch():
-    if not _patch_info['applied']:
-        from django.db.models import fields
-        from django.db.models.sql import subqueries
-        fields.AutoField.to_python = AutoField_to_python
-        fields.AutoField.get_db_prep_value = AutoField_get_db_prep_value
-        subqueries.UpdateQuery.execute_sql = UpdateQuery_execute_sql
-        subqueries.DeleteQuery.execute_sql = DeleteQuery_execute_sql
-        subqueries.InsertQuery.execute_sql = InsertQuery_execute_sql
-        _patch_info['applied'] = True
Add a comment to this file

testproject/__init__.py

Empty file added.

testproject/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

testproject/settings.py

+# Django settings for mongoway project.
+
+import os, sys
+os.environ['DJANGO_SETTINGS_MODULE'] = "settings"
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = 'django_mongodb'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+DATABASE_NAME = 'test'             # Or path to database file if using sqlite3.
+DATABASE_USER = ''             # Not used with sqlite3.
+DATABASE_PASSWORD = ''         # Not used with sqlite3.
+DATABASE_HOST = 'localhost'             # Set to empty string for localhost. Not used with sqlite3.
+DATABASE_PORT = '27017'             # Set to empty string for default. Not used with sqlite3.
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Samara'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'ru-ru'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'e_n1)bnq*pae+!0u79m0e%a7967kxotl+06i5+#g5j(ztu*of+'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+#     'django.template.loaders.eggs.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+)
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'testapp',
+)
Add a comment to this file

testproject/testapp/__init__.py

Empty file added.

testproject/testapp/models.py

+from django.db import models
+
+class CRUDTestModel(models.Model):
+    text = models.CharField(max_length=255)
+
+class NoAutoPK_CRUDTestModel(models.Model):
+    name = models.CharField(max_length=50, primary_key=True)
+    text = models.CharField(max_length=255)
+    

testproject/testapp/tests.py

+#-*- coding:utf-8 -*-
+
+from django.db import models, connection, DatabaseError
+from django.test import TestCase
+
+from testapp.models import CRUDTestModel, NoAutoPK_CRUDTestModel
+
+class TestCRUD(TestCase):
+
+    def setUp(self):
+        self.db = connection.cursor()
+        self.crud_col_name = CRUDTestModel._meta.db_table
+        self.nopk_crud_col_name = NoAutoPK_CRUDTestModel._meta.db_table
+
+    def tearDown(self):
+        self.db.drop_collection(self.crud_col_name)
+        self.db.drop_collection(self.nopk_crud_col_name)
+        self.db.logout()
+
+    def test_insert(self):
+        # Normal insert
+        for i in xrange(10):
+            obj = CRUDTestModel(text="text%d" % i)
+            obj.save()
+            self.assertTrue(isinstance(obj.pk, str))
+            query = {'text':"text%d" % i}
+            self.assertTrue(self.db[self.crud_col_name].find_one(query) is not None)
+        self.assertEqual(self.db[self.crud_col_name].find().count(), 10)
+        
+        # Insert with skipped primary key in model without AutoField
+        self.assertRaises(DatabaseError, lambda: NoAutoPK_CRUDTestModel(text="text").save())
+        self.assertEqual(self.db[self.nopk_crud_col_name].find().count(), 0)

testproject/urls.py

+from django.conf.urls.defaults import *
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    # Example:
+    # (r'^mongoway/', include('mongoway.foo.urls')),
+
+    # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
+    # to INSTALLED_APPS to enable admin documentation:
+    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    # Uncomment the next line to enable the admin:
+    (r'^admin/', include(admin.site.urls)),
+)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.