summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorBrian Rosner <brosner@gmail.com>2008-06-10 04:03:09 +0000
committerBrian Rosner <brosner@gmail.com>2008-06-10 04:03:09 +0000
commit4530a408c40647c195f27f81da1315e98e956f96 (patch)
treeed8290751a7e284b9f030f4da97b422067ca9c95 /django
parent3b92ced51856e740cd6fb3d3e5bb8ea932bd276e (diff)
newforms-admin: Merged from trunk up to [7602].
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7604 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/conf/global_settings.py2
-rw-r--r--django/contrib/auth/create_superuser.py94
-rw-r--r--django/contrib/auth/management/__init__.py (renamed from django/contrib/auth/management.py)10
-rw-r--r--django/contrib/auth/management/commands/__init__.py0
-rw-r--r--django/contrib/auth/management/commands/createsuperuser.py123
-rw-r--r--django/contrib/auth/models.py7
-rw-r--r--django/contrib/auth/tests/basic.py17
-rw-r--r--django/contrib/sessions/backends/base.py57
-rw-r--r--django/contrib/sessions/backends/cache.py8
-rw-r--r--django/contrib/sessions/backends/db.py2
-rw-r--r--django/contrib/sessions/middleware.py8
-rw-r--r--django/contrib/sessions/tests.py94
-rw-r--r--django/core/management/commands/loaddata.py35
-rw-r--r--django/core/management/sql.py2
-rw-r--r--django/core/serializers/base.py2
-rw-r--r--django/db/models/base.py13
-rw-r--r--django/db/models/options.py10
-rw-r--r--django/db/models/query.py4
-rw-r--r--django/db/models/sql/query.py16
19 files changed, 380 insertions, 124 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 8eea31b0db..cdf71c00dc 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -289,7 +289,7 @@ SESSION_COOKIE_DOMAIN = None # A string like ".lawren
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
-SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser.
+SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether a user's session cookie expires when they close their browser.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If None, the backend will use a sensible default.
diff --git a/django/contrib/auth/create_superuser.py b/django/contrib/auth/create_superuser.py
index 7b6cefd268..7b58678b78 100644
--- a/django/contrib/auth/create_superuser.py
+++ b/django/contrib/auth/create_superuser.py
@@ -1,94 +1,8 @@
"""
-Helper function for creating superusers in the authentication system.
-
-If run from the command line, this module lets you create a superuser
-interactively.
+Create a superuser from the command line. Deprecated; use manage.py
+createsuperuser instead.
"""
-from django.core import validators
-from django.contrib.auth.models import User
-import getpass
-import os
-import sys
-import re
-
-RE_VALID_USERNAME = re.compile('\w+$')
-
-def createsuperuser(username=None, email=None, password=None):
- """
- Helper function for creating a superuser from the command line. All
- arguments are optional and will be prompted-for if invalid or not given.
- """
- try:
- import pwd
- except ImportError:
- default_username = ''
- else:
- # Determine the current system user's username, to use as a default.
- default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower()
-
- # Determine whether the default username is taken, so we don't display
- # it as an option.
- if default_username:
- try:
- User.objects.get(username=default_username)
- except User.DoesNotExist:
- pass
- else:
- default_username = ''
-
- try:
- while 1:
- if not username:
- input_msg = 'Username'
- if default_username:
- input_msg += ' (Leave blank to use %r)' % default_username
- username = raw_input(input_msg + ': ')
- if default_username and username == '':
- username = default_username
- if not RE_VALID_USERNAME.match(username):
- sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
- username = None
- continue
- try:
- User.objects.get(username=username)
- except User.DoesNotExist:
- break
- else:
- sys.stderr.write("Error: That username is already taken.\n")
- username = None
- while 1:
- if not email:
- email = raw_input('E-mail address: ')
- try:
- validators.isValidEmail(email, None)
- except validators.ValidationError:
- sys.stderr.write("Error: That e-mail address is invalid.\n")
- email = None
- else:
- break
- while 1:
- if not password:
- password = getpass.getpass()
- password2 = getpass.getpass('Password (again): ')
- if password != password2:
- sys.stderr.write("Error: Your passwords didn't match.\n")
- password = None
- continue
- if password.strip() == '':
- sys.stderr.write("Error: Blank passwords aren't allowed.\n")
- password = None
- continue
- break
- except KeyboardInterrupt:
- sys.stderr.write("\nOperation cancelled.\n")
- sys.exit(1)
- u = User.objects.create_user(username, email, password)
- u.is_staff = True
- u.is_active = True
- u.is_superuser = True
- u.save()
- print "Superuser created successfully."
-
if __name__ == "__main__":
- createsuperuser()
+ from django.core.management import call_command
+ call_command("createsuperuser")
diff --git a/django/contrib/auth/management.py b/django/contrib/auth/management/__init__.py
index 2b4cb8bd19..8394bee5cd 100644
--- a/django/contrib/auth/management.py
+++ b/django/contrib/auth/management/__init__.py
@@ -32,7 +32,7 @@ def create_permissions(app, created_models, verbosity):
def create_superuser(app, created_models, verbosity, **kwargs):
from django.contrib.auth.models import User
- from django.contrib.auth.create_superuser import createsuperuser as do_create
+ from django.core.management import call_command
if User in created_models and kwargs.get('interactive', True):
msg = "\nYou just installed Django's auth system, which means you don't have " \
"any superusers defined.\nWould you like to create one now? (yes/no): "
@@ -42,8 +42,10 @@ def create_superuser(app, created_models, verbosity, **kwargs):
confirm = raw_input('Please enter either "yes" or "no": ')
continue
if confirm == 'yes':
- do_create()
+ call_command("createsuperuser", interactive=True)
break
-dispatcher.connect(create_permissions, signal=signals.post_syncdb)
-dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb)
+if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]:
+ dispatcher.connect(create_permissions, signal=signals.post_syncdb)
+if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]:
+ dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb) \ No newline at end of file
diff --git a/django/contrib/auth/management/commands/__init__.py b/django/contrib/auth/management/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/auth/management/commands/__init__.py
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
new file mode 100644
index 0000000000..4299762c74
--- /dev/null
+++ b/django/contrib/auth/management/commands/createsuperuser.py
@@ -0,0 +1,123 @@
+"""
+Management utility to create superusers.
+"""
+
+import getpass
+import os
+import re
+import sys
+from optparse import make_option
+from django.contrib.auth.models import User, UNUSABLE_PASSWORD
+from django.core import validators
+from django.core.management.base import BaseCommand, CommandError
+
+RE_VALID_USERNAME = re.compile('\w+$')
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list + (
+ make_option('--username', dest='username', default=None,
+ help='Specifies the username for the superuser.'),
+ make_option('--email', dest='email', default=None,
+ help='Specifies the email address for the superuser.'),
+ make_option('--noinput', action='store_false', dest='interactive', default=True,
+ help='Tells Django to NOT prompt the user for input of any kind. ' \
+ 'You must use --username and --email with --noinput, and ' \
+ 'superusers created with --noinput will not be able to log in ' \
+ 'until they\'re given a valid password.'),
+ )
+ help = 'Used to create a superuser.'
+
+ def handle(self, *args, **options):
+ username = options.get('username', None)
+ email = options.get('email', None)
+ interactive = options.get('interactive')
+
+ # Do quick and dirty validation if --noinput
+ if not interactive:
+ if not username or not email:
+ raise CommandError("You must use --username and --email with --noinput.")
+ if not RE_VALID_USERNAME.match(username):
+ raise CommandError("Invalid username. Use only letters, digits, and underscores")
+ try:
+ validators.isValidEmail(email, None)
+ except validators.ValidationError:
+ raise CommandError("Invalid email address.")
+
+ password = ''
+
+ # Try to determine the current system user's username to use as a default.
+ try:
+ import pwd
+ except ImportError:
+ default_username = ''
+ else:
+ default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower()
+
+ # Determine whether the default username is taken, so we don't display
+ # it as an option.
+ if default_username:
+ try:
+ User.objects.get(username=default_username)
+ except User.DoesNotExist:
+ pass
+ else:
+ default_username = ''
+
+ # Prompt for username/email/password. Enclose this whole thing in a
+ # try/except to trap for a keyboard interrupt and exit gracefully.
+ if interactive:
+ try:
+
+ # Get a username
+ while 1:
+ if not username:
+ input_msg = 'Username'
+ if default_username:
+ input_msg += ' (Leave blank to use %r)' % default_username
+ username = raw_input(input_msg + ': ')
+ if default_username and username == '':
+ username = default_username
+ if not RE_VALID_USERNAME.match(username):
+ sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
+ username = None
+ continue
+ try:
+ User.objects.get(username=username)
+ except User.DoesNotExist:
+ break
+ else:
+ sys.stderr.write("Error: That username is already taken.\n")
+ username = None
+
+ # Get an email
+ while 1:
+ if not email:
+ email = raw_input('E-mail address: ')
+ try:
+ validators.isValidEmail(email, None)
+ except validators.ValidationError:
+ sys.stderr.write("Error: That e-mail address is invalid.\n")
+ email = None
+ else:
+ break
+
+ # Get a password
+ while 1:
+ if not password:
+ password = getpass.getpass()
+ password2 = getpass.getpass('Password (again): ')
+ if password != password2:
+ sys.stderr.write("Error: Your passwords didn't match.\n")
+ password = None
+ continue
+ if password.strip() == '':
+ sys.stderr.write("Error: Blank passwords aren't allowed.\n")
+ password = None
+ continue
+ break
+ except KeyboardInterrupt:
+ sys.stderr.write("\nOperation cancelled.\n")
+ sys.exit(1)
+
+ User.objects.create_superuser(username, email, password)
+ print "Superuser created successfully."
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index e3f7541dda..56b4cbc082 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -113,6 +113,13 @@ class UserManager(models.Manager):
user.save()
return user
+ def create_superuser(self, username, email, password):
+ u = self.create_user(username, email, password)
+ u.is_staff = True
+ u.is_active = True
+ u.is_superuser = True
+ u.save()
+
def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
"Generates a random password with the given length and given allowed_chars"
# Note that default value of allowed_chars does not have "I" or letters
diff --git a/django/contrib/auth/tests/basic.py b/django/contrib/auth/tests/basic.py
index 950815e791..8667ca9e5a 100644
--- a/django/contrib/auth/tests/basic.py
+++ b/django/contrib/auth/tests/basic.py
@@ -36,4 +36,21 @@ False
[]
>>> a.user_permissions.all()
[]
+
+#
+# Tests for createsuperuser management command.
+# It's nearly impossible to test the interactive mode -- a command test helper
+# would be needed (and *awesome*) -- so just test the non-interactive mode.
+# This covers most of the important validation, but not all.
+#
+>>> from django.core.management import call_command
+
+>>> call_command("createsuperuser", noinput=True, username="joe", email="joe@somewhere.org")
+Superuser created successfully.
+
+>>> u = User.objects.get(username="joe")
+>>> u.email
+u'joe@somewhere.org'
+>>> u.password
+u'!'
""" \ No newline at end of file
diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py
index b8726fd2bd..1063760915 100644
--- a/django/contrib/sessions/backends/base.py
+++ b/django/contrib/sessions/backends/base.py
@@ -4,6 +4,7 @@ import os
import random
import sys
import time
+from datetime import datetime, timedelta
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
@@ -128,6 +129,62 @@ class SessionBase(object):
_session = property(_get_session)
+ def get_expiry_age(self):
+ """Get the number of seconds until the session expires."""
+ expiry = self.get('_session_expiry')
+ if not expiry: # Checks both None and 0 cases
+ return settings.SESSION_COOKIE_AGE
+ if not isinstance(expiry, datetime):
+ return expiry
+ delta = expiry - datetime.now()
+ return delta.days * 86400 + delta.seconds
+
+ def get_expiry_date(self):
+ """Get session the expiry date (as a datetime object)."""
+ expiry = self.get('_session_expiry')
+ if isinstance(expiry, datetime):
+ return expiry
+ if not expiry: # Checks both None and 0 cases
+ expiry = settings.SESSION_COOKIE_AGE
+ return datetime.now() + timedelta(seconds=expiry)
+
+ def set_expiry(self, value):
+ """
+ Sets a custom expiration for the session. ``value`` can be an integer, a
+ Python ``datetime`` or ``timedelta`` object or ``None``.
+
+ If ``value`` is an integer, the session will expire after that many
+ seconds of inactivity. If set to ``0`` then the session will expire on
+ browser close.
+
+ If ``value`` is a ``datetime`` or ``timedelta`` object, the session
+ will expire at that specific future time.
+
+ If ``value`` is ``None``, the session uses the global session expiry
+ policy.
+ """
+ if value is None:
+ # Remove any custom expiration for this session.
+ try:
+ del self['_session_expiry']
+ except KeyError:
+ pass
+ return
+ if isinstance(value, timedelta):
+ value = datetime.now() + value
+ self['_session_expiry'] = value
+
+ def get_expire_at_browser_close(self):
+ """
+ Returns ``True`` if the session is set to expire when the browser
+ closes, and ``False`` if there's an expiry date. Use
+ ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
+ date/age, if there is one.
+ """
+ if self.get('_session_expiry') is None:
+ return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+ return self.get('_session_expiry') == 0
+
# Methods that child classes must implement.
def exists(self, session_key):
diff --git a/django/contrib/sessions/backends/cache.py b/django/contrib/sessions/backends/cache.py
index c3e641e691..7626163a13 100644
--- a/django/contrib/sessions/backends/cache.py
+++ b/django/contrib/sessions/backends/cache.py
@@ -4,23 +4,23 @@ from django.core.cache import cache
class SessionStore(SessionBase):
"""
- A cache-based session store.
+ A cache-based session store.
"""
def __init__(self, session_key=None):
self._cache = cache
super(SessionStore, self).__init__(session_key)
-
+
def load(self):
session_data = self._cache.get(self.session_key)
return session_data or {}
def save(self):
- self._cache.set(self.session_key, self._session, settings.SESSION_COOKIE_AGE)
+ self._cache.set(self.session_key, self._session, self.get_expiry_age())
def exists(self, session_key):
if self._cache.get(session_key):
return True
return False
-
+
def delete(self, session_key):
self._cache.delete(session_key) \ No newline at end of file
diff --git a/django/contrib/sessions/backends/db.py b/django/contrib/sessions/backends/db.py
index 0f79d9ee1a..b1c1097865 100644
--- a/django/contrib/sessions/backends/db.py
+++ b/django/contrib/sessions/backends/db.py
@@ -41,7 +41,7 @@ class SessionStore(SessionBase):
Session.objects.create(
session_key = self.session_key,
session_data = self.encode(self._session),
- expire_date = datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
+ expire_date = self.get_expiry_date()
)
def delete(self, session_key):
diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py
index 2af2312e76..a7b376dde0 100644
--- a/django/contrib/sessions/middleware.py
+++ b/django/contrib/sessions/middleware.py
@@ -26,14 +26,14 @@ class SessionMiddleware(object):
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
- if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
+ if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
- max_age = settings.SESSION_COOKIE_AGE
- expires_time = time.time() + settings.SESSION_COOKIE_AGE
+ max_age = request.session.get_expiry_age()
+ expires_time = time.time() + max_age
expires = cookie_date(expires_time)
- # Save the seesion data and refresh the client cookie.
+ # Save the session data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index b2c664ce7b..0f162b211f 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -88,6 +88,100 @@ False
>>> s.pop('some key', 'does not exist')
'does not exist'
+
+#########################
+# Custom session expiry #
+#########################
+
+>>> from django.conf import settings
+>>> from datetime import datetime, timedelta
+
+>>> td10 = timedelta(seconds=10)
+
+# A normal session has a max age equal to settings
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# So does a custom session with an idle expiration time of 0 (but it'll expire
+# at browser close)
+>>> s.set_expiry(0)
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Custom session idle expiration time
+>>> s.set_expiry(10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Custom session fixed expiry date (timedelta)
+>>> s.set_expiry(td10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Custom session fixed expiry date (fixed datetime)
+>>> s.set_expiry(datetime.now() + td10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Set back to default session age
+>>> s.set_expiry(None)
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Allow to set back to default session age even if no alternate has been set
+>>> s.set_expiry(None)
+
+
+# We're changing the setting then reverting back to the original setting at the
+# end of these tests.
+>>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+False
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+True
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
"""
if __name__ == '__main__':
diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index d06b131d6f..193bb26ccf 100644
--- a/django/core/management/commands/loaddata.py
+++ b/django/core/management/commands/loaddata.py
@@ -32,6 +32,7 @@ class Command(BaseCommand):
# Keep a count of the installed objects and fixtures
fixture_count = 0
object_count = 0
+ objects_per_fixture = []
models = set()
humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
@@ -60,11 +61,16 @@ class Command(BaseCommand):
else:
formats = []
- if verbosity >= 2:
- if formats:
+ if formats:
+ if verbosity > 1:
print "Loading '%s' fixtures..." % fixture_name
- else:
- print "Skipping fixture '%s': %s is not a known serialization format" % (fixture_name, format)
+ else:
+ sys.stderr.write(
+ self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format." %
+ (fixture_name, format)))
+ transaction.rollback()
+ transaction.leave_transaction_management()
+ return
if os.path.isabs(fixture_name):
fixture_dirs = [fixture_name]
@@ -93,6 +99,7 @@ class Command(BaseCommand):
return
else:
fixture_count += 1
+ objects_per_fixture.append(0)
if verbosity > 0:
print "Installing %s fixture '%s' from %s." % \
(format, fixture_name, humanize(fixture_dir))
@@ -100,6 +107,7 @@ class Command(BaseCommand):
objects = serializers.deserialize(format, fixture)
for obj in objects:
object_count += 1
+ objects_per_fixture[-1] += 1
models.add(obj.object.__class__)
obj.save()
label_found = True
@@ -117,10 +125,23 @@ class Command(BaseCommand):
return
fixture.close()
except:
- if verbosity >= 2:
+ if verbosity > 1:
print "No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir))
+
+ # If any of the fixtures we loaded contain 0 objects, assume that an
+ # error was encountered during fixture loading.
+ if 0 in objects_per_fixture:
+ sys.stderr.write(
+ self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" %
+ (fixture_name)))
+ transaction.rollback()
+ transaction.leave_transaction_management()
+ return
+
+ # If we found even one object in a fixture, we need to reset the
+ # database sequences.
if object_count > 0:
sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
if sequence_sql:
@@ -128,12 +149,12 @@ class Command(BaseCommand):
print "Resetting sequences"
for line in sequence_sql:
cursor.execute(line)
-
+
transaction.commit()
transaction.leave_transaction_management()
if object_count == 0:
- if verbosity >= 2:
+ if verbosity > 1:
print "No fixtures found."
else:
if verbosity > 0:
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index 574be5a1ee..c635fcab8a 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -446,7 +446,7 @@ def custom_sql_for_model(model):
fp = open(sql_file, 'U')
for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
# Remove any comments from the file
- statement = re.sub(ur"--.*[\n\Z]", "", statement)
+ statement = re.sub(ur"--.*([\n\Z]|$)", "", statement)
if statement.strip():
output.append(statement + u";")
fp.close()
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index a79497ecec..e22a35815b 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -38,7 +38,7 @@ class Serializer(object):
self.start_serialization()
for obj in queryset:
self.start_object(obj)
- for field in obj._meta.fields:
+ for field in obj._meta.local_fields:
if field.serialize:
if field.rel is None:
if self.selected_fields is None or field.attname in self.selected_fields:
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 01c2f31794..a253f38f47 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -287,12 +287,17 @@ class Model(object):
meta = cls._meta
signal = False
- for parent, field in meta.parents.items():
- self.save_base(raw, parent)
- setattr(self, field.attname, self._get_pk_val(parent._meta))
+ # If we are in a raw save, save the object exactly as presented.
+ # That means that we don't try to be smart about saving attributes
+ # that might have come from the parent class - we just save the
+ # attributes we have been given to the class we have been given.
+ if not raw:
+ for parent, field in meta.parents.items():
+ self.save_base(raw, parent)
+ setattr(self, field.attname, self._get_pk_val(parent._meta))
non_pks = [f for f in meta.local_fields if not f.primary_key]
-
+
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
pk_val = self._get_pk_val(meta)
# Note: the comparison with '' is required for compatibility with
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 3948a5fd10..c78ab1cd48 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -55,8 +55,12 @@ class Options(object):
# Next, apply any overridden values from 'class Meta'.
if self.meta:
meta_attrs = self.meta.__dict__.copy()
- del meta_attrs['__module__']
- del meta_attrs['__doc__']
+ for name in self.meta.__dict__:
+ # Ignore any private attributes that Django doesn't care about.
+ # NOTE: We can't modify a dictionary's contents while looping
+ # over it, so we loop over the *original* dictionary instead.
+ if name.startswith('_'):
+ del meta_attrs[name]
for attr_name in DEFAULT_NAMES:
if attr_name in meta_attrs:
setattr(self, attr_name, meta_attrs.pop(attr_name))
@@ -97,7 +101,7 @@ class Options(object):
# field.
field = self.parents.value_for_index(0)
field.primary_key = True
- self.pk = field
+ self.setup_pk(field)
else:
auto = AutoField(verbose_name='ID', primary_key=True,
auto_created=True)
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 6b341ba9ab..12731caa94 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -292,6 +292,8 @@ class QuerySet(object):
Updates all elements in the current QuerySet, setting all the given
fields to the appropriate values.
"""
+ assert self.query.can_filter(), \
+ "Cannot update a query once a slice has been taken."
query = self.query.clone(sql.UpdateQuery)
query.add_update_values(kwargs)
query.execute_sql(None)
@@ -306,6 +308,8 @@ class QuerySet(object):
code (it requires too much poking around at model internals to be
useful at that level).
"""
+ assert self.query.can_filter(), \
+ "Cannot update a query once a slice has been taken."
query = self.query.clone(sql.UpdateQuery)
query.add_update_fields(values)
query.execute_sql(None)
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index a6957bab7b..3044882a86 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -851,7 +851,7 @@ class Query(object):
return alias
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
- used=None, requested=None, restricted=None):
+ used=None, requested=None, restricted=None, nullable=None):
"""
Fill in the information needed for a select_related query. The current
depth is measured as the number of connections away from the root model
@@ -883,6 +883,10 @@ class Query(object):
(not restricted and f.null) or f.rel.parent_link):
continue
table = f.rel.to._meta.db_table
+ if nullable or f.null:
+ promote = True
+ else:
+ promote = False
if model:
int_opts = opts
alias = root_alias
@@ -891,12 +895,12 @@ class Query(object):
int_opts = int_model._meta
alias = self.join((alias, int_opts.db_table, lhs_col,
int_opts.pk.column), exclusions=used,
- promote=f.null)
+ promote=promote)
else:
alias = root_alias
alias = self.join((alias, table, f.column,
f.rel.get_related_field().column), exclusions=used,
- promote=f.null)
+ promote=promote)
used.add(alias)
self.related_select_cols.extend([(alias, f2.column)
for f2 in f.rel.to._meta.fields])
@@ -905,8 +909,12 @@ class Query(object):
next = requested.get(f.name, {})
else:
next = False
+ if f.null is not None:
+ new_nullable = f.null
+ else:
+ new_nullable = None
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
- used, next, restricted)
+ used, next, restricted, new_nullable)
def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
can_reuse=None):