mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
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
This commit is contained in:
parent
3b92ced518
commit
4530a408c4
4
AUTHORS
4
AUTHORS
@ -57,6 +57,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
David Ascher <http://ascher.ca/>
|
||||
Jökull Sólberg Auðunsson <jokullsolberg@gmail.com>
|
||||
Arthur <avandorp@gmail.com>
|
||||
av0000@mail.ru
|
||||
David Avsajanishvili <avsd05@gmail.com>
|
||||
axiak@mit.edu
|
||||
Niran Babalola <niran@niran.org>
|
||||
@ -79,6 +80,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
brut.alll@gmail.com
|
||||
btoll@bestweb.net
|
||||
Jonathan Buchanan <jonathan.buchanan@gmail.com>
|
||||
Keith Bussell <kbussell@gmail.com>
|
||||
Juan Manuel Caicedo <juan.manuel.caicedo@gmail.com>
|
||||
Trevor Caira <trevor@caira.com>
|
||||
Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
|
||||
@ -368,7 +370,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Makoto Tsuyuki <mtsuyuki@gmail.com>
|
||||
tt@gurgle.no
|
||||
David Tulig <david.tulig@gmail.com>
|
||||
Amit Upadhyay
|
||||
Amit Upadhyay <http://www.amitu.com/blog/>
|
||||
Geert Vanderkelen
|
||||
I.S. van Oostveen <v.oostveen@idca.nl>
|
||||
viestards.lists@gmail.com
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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)
|
0
django/contrib/auth/management/commands/__init__.py
Normal file
0
django/contrib/auth/management/commands/__init__.py
Normal file
123
django/contrib/auth/management/commands/createsuperuser.py
Normal file
123
django/contrib/auth/management/commands/createsuperuser.py
Normal file
@ -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."
|
@ -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
|
||||
|
@ -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'!'
|
||||
"""
|
@ -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):
|
||||
|
@ -15,7 +15,7 @@ class SessionStore(SessionBase):
|
||||
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):
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
|
@ -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__':
|
||||
|
@ -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:
|
||||
@ -133,7 +154,7 @@ class Command(BaseCommand):
|
||||
transaction.leave_transaction_management()
|
||||
|
||||
if object_count == 0:
|
||||
if verbosity >= 2:
|
||||
if verbosity > 1:
|
||||
print "No fixtures found."
|
||||
else:
|
||||
if verbosity > 0:
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -287,9 +287,14 @@ 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]
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -263,14 +263,25 @@ Creating superusers
|
||||
-------------------
|
||||
|
||||
``manage.py syncdb`` prompts you to create a superuser the first time you run
|
||||
it after adding ``'django.contrib.auth'`` to your ``INSTALLED_APPS``. But if
|
||||
you need to create a superuser after that via the command line, you can use the
|
||||
``create_superuser.py`` utility. Just run this command::
|
||||
it after adding ``'django.contrib.auth'`` to your ``INSTALLED_APPS``. If you need
|
||||
to create a superuser at a later date, you can use a command line utility.
|
||||
|
||||
**New in Django development version.**::
|
||||
|
||||
manage.py createsuperuser --username=joe --email=joe@example.com
|
||||
|
||||
You will be prompted for a password. Once entered, the user is created. If you
|
||||
leave off the ``--username`` or the ``--email`` option, It will prompt you for
|
||||
those values as well.
|
||||
|
||||
If you're using an older release of Django, the old way of creating a superuser
|
||||
on the command line still works::
|
||||
|
||||
python /path/to/django/contrib/auth/create_superuser.py
|
||||
|
||||
Make sure to substitute ``/path/to/`` with the path to the Django codebase on
|
||||
your filesystem.
|
||||
Where ``/path/to`` is the path to the Django codebase on your filesystem. The
|
||||
``manage.py`` command is prefered since it'll figure out the correct path and
|
||||
environment for you.
|
||||
|
||||
Storing additional information about users
|
||||
------------------------------------------
|
||||
|
@ -109,6 +109,31 @@ the program name (``psql``, ``mysql``, ``sqlite3``) will find the program in
|
||||
the right place. There's no way to specify the location of the program
|
||||
manually.
|
||||
|
||||
createsuperuser
|
||||
---------------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Creates a superuser account (a user who has all permissions). This is
|
||||
useful if you need to create an initial superuser account but did not
|
||||
do so during ``syncdb``, or if you need to programmatically generate
|
||||
superuser accounts for your site(s).
|
||||
|
||||
When run interactively, this command will prompt for a password for
|
||||
the new superuser account; when run non-interactively, no password
|
||||
will be set and the superuser account will not be able to log in until
|
||||
a password has been manually set for it.
|
||||
|
||||
The username and e-mail address for the new account can be supplied by
|
||||
using the ``--username`` and ``--email`` arguments on the command
|
||||
line; if not supplied, ``createsuperuser`` will prompt for them when
|
||||
running interactively.
|
||||
|
||||
This command is only available if Django's `authentication system`_
|
||||
(``django.contrib.auth``) is installed.
|
||||
|
||||
.. _authentication system: ../authentication/
|
||||
|
||||
diffsettings
|
||||
------------
|
||||
|
||||
|
93
docs/faq.txt
93
docs/faq.txt
@ -228,13 +228,14 @@ Short answer: When we're comfortable with Django's APIs, have added all
|
||||
features that we feel are necessary to earn a "1.0" status, and are ready to
|
||||
begin maintaining backwards compatibility.
|
||||
|
||||
The merging of Django's `magic-removal branch`_ went a long way toward Django
|
||||
1.0.
|
||||
The merging of Django's `Queryset Refactor branch`_ went a long way toward Django
|
||||
1.0. Merging the `Newforms Admin branch` will be another important step.
|
||||
|
||||
Of course, you should note that `quite a few production sites`_ use Django in
|
||||
its current status. Don't let the lack of a 1.0 turn you off.
|
||||
|
||||
.. _magic-removal branch: http://code.djangoproject.com/wiki/RemovingTheMagic
|
||||
.. _Queryset Refactor branch: http://code.djangoproject.com/wiki/QuerysetRefactorBranch
|
||||
.. _Newforms Admin branch: http://code.djangoproject.com/wiki/NewformsAdminBranch
|
||||
.. _quite a few production sites: http://code.djangoproject.com/wiki/DjangoPoweredSites
|
||||
|
||||
How can I download the Django documentation to read it offline?
|
||||
@ -259,7 +260,9 @@ Where can I find Django developers for hire?
|
||||
Consult our `developers for hire page`_ for a list of Django developers who
|
||||
would be happy to help you.
|
||||
|
||||
You might also be interested in posting a job to http://www.gypsyjobs.com/ .
|
||||
You might also be interested in posting a job to http://djangogigs.com/ .
|
||||
If you want to find Django-capable people in your local area, try
|
||||
http://djangopeople.net/ .
|
||||
|
||||
.. _developers for hire page: http://code.djangoproject.com/wiki/DevelopersForHire
|
||||
|
||||
@ -643,6 +646,81 @@ You can also use the Python API. See `creating users`_ for full info.
|
||||
|
||||
.. _creating users: ../authentication/#creating-users
|
||||
|
||||
Getting Help
|
||||
============
|
||||
|
||||
How do I do X? Why doesn't Y work? Where can I go to get help?
|
||||
--------------------------------------------------------------
|
||||
|
||||
If this FAQ doesn't contain an answer to your question, you might want to
|
||||
try the `django-users mailing list`_. Feel free to ask any question related
|
||||
to installing, using, or debugging Django.
|
||||
|
||||
If you prefer IRC, the `#django IRC channel`_ on freenode is an active
|
||||
community of helpful individuals who may be able to solve your problem.
|
||||
|
||||
.. _`django-users mailing list`: http://groups.google.com/group/django-users
|
||||
.. _`#django IRC channel`: irc://irc.freenode.net/django
|
||||
|
||||
Why hasn't my message appeared on django-users?
|
||||
-----------------------------------------------
|
||||
|
||||
django-users_ has a lot of subscribers. This is good for the community, as
|
||||
there are lot of people that can contribute answers to questions.
|
||||
Unfortunately, it also means that django-users_ is an attractive target for
|
||||
spammers.
|
||||
|
||||
In order to combat the spam problem, when you join the django-users_ mailing
|
||||
list, we manually moderate the first message you send to the list. This means
|
||||
that spammers get caught, but it also means that your first question to the
|
||||
list might take a little longer to get answered. We apologize for any
|
||||
inconvenience that this policy may cause.
|
||||
|
||||
.. _django-users: http://groups.google.com/group/django-users
|
||||
|
||||
Nobody on django-users answered my question? What should I do?
|
||||
--------------------------------------------------------------
|
||||
|
||||
Wait. Ask again later. Try making your question more specific, or provide
|
||||
a better example of your problem.
|
||||
|
||||
Remember, the readers of django-users_ are all volunteers. If nobody has
|
||||
answered your question, it may be because nobody knows the answer, it may
|
||||
be because nobody can understand the question, or it may be that everybody
|
||||
that can help is extremely busy.
|
||||
|
||||
Resist any temptation to mail the `django-developers mailing list`_ in an
|
||||
attempt to get an answer to your question. django-developers_ is for discussing
|
||||
the development of Django itself. Attempts to use django-developers_ as
|
||||
a second-tier support mechanism will not be met an enthusiastic response.
|
||||
|
||||
.. _`django-developers mailing list`: http://groups.google.com/group/django-developers
|
||||
.. _django-developers: http://groups.google.com/group/django-developers
|
||||
|
||||
I think I've found a bug! What should I do?
|
||||
-------------------------------------------
|
||||
|
||||
Detailed instructions on how to handle a potential bug can be found in our
|
||||
`Guide to contributing to Django`_.
|
||||
|
||||
.. _`Guide to contributing to Django`: ../contributing/#reporting-bugs
|
||||
|
||||
I think I've found a security problem! What should I do?
|
||||
--------------------------------------------------------
|
||||
|
||||
If you think you have found a security problem with Django, please send
|
||||
a message to security@djangoproject.com. This is a private list only
|
||||
open to long-time, highly trusted Django developers, and its archives
|
||||
are not publicly readable.
|
||||
|
||||
Due to the sensitive nature of security issues, we ask that if you think you
|
||||
have found a security problem, *please* don't send a message to one of the
|
||||
public mailing lists. Django has a `policy for handling security issues`_;
|
||||
while a defect is outstanding, we would like to minimize any damage that
|
||||
could be inflicted through public knowledge of that defect.
|
||||
|
||||
.. _`policy for handling security issues`: ../contributing/#reporting-security-issues
|
||||
|
||||
Contributing code
|
||||
=================
|
||||
|
||||
@ -652,7 +730,7 @@ How can I get started contributing code to Django?
|
||||
Thanks for asking! We've written an entire document devoted to this question.
|
||||
It's titled `Contributing to Django`_.
|
||||
|
||||
.. _Contributing to Django: ../contributing/
|
||||
.. _`Contributing to Django`: ../contributing/
|
||||
|
||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||
--------------------------------------------------------------------------------------------
|
||||
@ -664,6 +742,11 @@ ignored" and "a ticket has not been attended to yet." Django's ticket system
|
||||
contains hundreds of open tickets, of various degrees of impact on end-user
|
||||
functionality, and Django's developers have to review and prioritize.
|
||||
|
||||
On top of that - the team working on Django are all volunteers. As a result,
|
||||
the amount of time that we have to work on Django is limited, and will vary
|
||||
from week to week depending on how much spare time we have. If we are busy, we
|
||||
may not be able to spend as much time on Django as we might want.
|
||||
|
||||
Besides, if your feature request stands no chance of inclusion in Django, we
|
||||
won't ignore it -- we'll just close the ticket. So if your ticket is still
|
||||
open, it doesn't mean we're ignoring you; it just means we haven't had time to
|
||||
|
@ -63,6 +63,41 @@ be serialized.
|
||||
doesn't specify all the fields that are required by a model, the deserializer
|
||||
will not be able to save deserialized instances.
|
||||
|
||||
Inherited Models
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have a model that is defined using an `abstract base class`_, you don't
|
||||
have to do anything special to serialize that model. Just call the serializer
|
||||
on the object (or objects) that you want to serialize, and the output will be
|
||||
a complete representation of the serialized object.
|
||||
|
||||
However, if you have a model that uses `multi-table inheritance`_, you also
|
||||
need to serialize all of the base classes for the model. This is because only
|
||||
the fields that are locally defined on the model will be serialized. For
|
||||
example, consider the following models::
|
||||
|
||||
class Place(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
class Restaurant(Place):
|
||||
serves_hot_dogs = models.BooleanField()
|
||||
|
||||
If you only serialize the Restaurant model::
|
||||
|
||||
data = serializers.serialize('xml', Restaurant.objects.all())
|
||||
|
||||
the fields on the serialized output will only contain the `serves_hot_dogs`
|
||||
attribute. The `name` attribute of the base class will be ignored.
|
||||
|
||||
In order to fully serialize your Restaurant instances, you will need to
|
||||
serialize the Place models as well::
|
||||
|
||||
all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
|
||||
data = serializers.serialize('xml', all_objects)
|
||||
|
||||
.. _abstract base class: http://www.djangoproject.com/documentation/model-api/#abstract-base-classes
|
||||
.. _multi-table inheritance: http://www.djangoproject.com/documentation/model-api/#multi-table-inheritance
|
||||
|
||||
Deserializing data
|
||||
------------------
|
||||
|
||||
|
@ -80,19 +80,24 @@ attribute, which is a dictionary-like object. You can read it and write to it.
|
||||
It implements the following standard dictionary methods:
|
||||
|
||||
* ``__getitem__(key)``
|
||||
|
||||
Example: ``fav_color = request.session['fav_color']``
|
||||
|
||||
* ``__setitem__(key, value)``
|
||||
|
||||
Example: ``request.session['fav_color'] = 'blue'``
|
||||
|
||||
* ``__delitem__(key)``
|
||||
|
||||
Example: ``del request.session['fav_color']``. This raises ``KeyError``
|
||||
if the given ``key`` isn't already in the session.
|
||||
|
||||
* ``__contains__(key)``
|
||||
|
||||
Example: ``'fav_color' in request.session``
|
||||
|
||||
* ``get(key, default=None)``
|
||||
|
||||
Example: ``fav_color = request.session.get('fav_color', 'red')``
|
||||
|
||||
* ``keys()``
|
||||
@ -101,23 +106,70 @@ It implements the following standard dictionary methods:
|
||||
|
||||
* ``setdefault()`` (**New in Django development version**)
|
||||
|
||||
It also has these three methods:
|
||||
It also has these methods:
|
||||
|
||||
* ``set_test_cookie()``
|
||||
|
||||
Sets a test cookie to determine whether the user's browser supports
|
||||
cookies. Due to the way cookies work, you won't be able to test this
|
||||
until the user's next page request. See "Setting test cookies" below for
|
||||
more information.
|
||||
|
||||
* ``test_cookie_worked()``
|
||||
|
||||
Returns either ``True`` or ``False``, depending on whether the user's
|
||||
browser accepted the test cookie. Due to the way cookies work, you'll
|
||||
have to call ``set_test_cookie()`` on a previous, separate page request.
|
||||
See "Setting test cookies" below for more information.
|
||||
|
||||
* ``delete_test_cookie()``
|
||||
|
||||
Deletes the test cookie. Use this to clean up after yourself.
|
||||
|
||||
* ``set_expiry(value)``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Sets the expiration time for the session. You can pass a number of
|
||||
different values:
|
||||
|
||||
* If ``value`` is an integer, the session will expire after that
|
||||
many seconds of inactivity. For example, calling
|
||||
``request.session.set_expiry(300)`` would make the session expire
|
||||
in 5 minutes.
|
||||
|
||||
* If ``value`` is a ``datetime`` or ``timedelta`` object, the
|
||||
session will expire at that specific time.
|
||||
|
||||
* If ``value`` is ``0`` then the user's session cookie will expire
|
||||
when their browser is closed.
|
||||
|
||||
* If ``value`` is ``None``, the session reverts to using the global
|
||||
session expiry policy.
|
||||
|
||||
* ``get_expiry_age()``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns the number of seconds until this session expires. For sessions
|
||||
with no custom expiration (or those set to expire at browser close), this
|
||||
will equal ``settings.SESSION_COOKIE_AGE``.
|
||||
|
||||
* ``get_expiry_date()``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns the date this session will expire. For sessions with no custom
|
||||
expiration (or those set to expire at browser close), this will equal the
|
||||
date ``settings.SESSION_COOKIE_AGE`` seconds from now.
|
||||
|
||||
* ``get_expire_at_browser_close()``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns either ``True`` or ``False``, depending on whether the user's
|
||||
session cookie will expire when their browser is closed.
|
||||
|
||||
You can edit ``request.session`` at any point in your view. You can edit it
|
||||
multiple times.
|
||||
|
||||
@ -278,6 +330,12 @@ browser-length cookies -- cookies that expire as soon as the user closes his or
|
||||
her browser. Use this if you want people to have to log in every time they open
|
||||
a browser.
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
This setting is a global default and can be overwritten at a per-session level
|
||||
by explicitly calling ``request.session.set_expiry()`` as described above in
|
||||
`using sessions in views`_.
|
||||
|
||||
Clearing the session table
|
||||
==========================
|
||||
|
||||
|
@ -147,8 +147,13 @@ Test constructor for Restaurant.
|
||||
>>> c.save()
|
||||
>>> ir = ItalianRestaurant(name='Ristorante Miron', address='1234 W. Ash', serves_hot_dogs=False, serves_pizza=False, serves_gnocchi=True, rating=4, chef=c)
|
||||
>>> ir.save()
|
||||
>>> ItalianRestaurant.objects.filter(address='1234 W. Ash')
|
||||
[<ItalianRestaurant: Ristorante Miron the italian restaurant>]
|
||||
|
||||
>>> ir.address = '1234 W. Elm'
|
||||
>>> ir.save()
|
||||
>>> ItalianRestaurant.objects.filter(address='1234 W. Elm')
|
||||
[<ItalianRestaurant: Ristorante Miron the italian restaurant>]
|
||||
|
||||
# Make sure Restaurant and ItalianRestaurant have the right fields in the right
|
||||
# order.
|
||||
|
@ -63,5 +63,12 @@ a manager method.
|
||||
>>> DataPoint.objects.values('value').distinct()
|
||||
[{'value': u'thing'}]
|
||||
|
||||
We do not support update on already sliced query sets.
|
||||
|
||||
>>> DataPoint.objects.all()[:2].update(another_value='another thing')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: Cannot update a query once a slice has been taken.
|
||||
|
||||
"""
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
This data shouldn't load, as it's of an unknown file format.
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<django-objcts version="1.0">
|
||||
<objct pk="2" model="fixtures.article">
|
||||
<field type="CharField" name="headline">Poker on TV is great!</field>
|
||||
<field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field>
|
||||
</objct>
|
||||
</django-objcts>
|
@ -71,4 +71,27 @@ __test__ = {'API_TESTS':"""
|
||||
>>> Absolute.load_count
|
||||
1
|
||||
|
||||
###############################################
|
||||
# Test for ticket #4371 -- fixture loading fails silently in testcases
|
||||
# Validate that error conditions are caught correctly
|
||||
|
||||
# redirect stderr for the next few tests...
|
||||
>>> import sys
|
||||
>>> savestderr = sys.stderr
|
||||
>>> sys.stderr = sys.stdout
|
||||
|
||||
# Loading data of an unknown format should fail
|
||||
>>> management.call_command('loaddata', 'bad_fixture1.unkn', verbosity=0)
|
||||
Problem installing fixture 'bad_fixture1': unkn is not a known serialization format.
|
||||
|
||||
# Loading a fixture file with invalid data using explicit filename
|
||||
>>> management.call_command('loaddata', 'bad_fixture2.xml', verbosity=0)
|
||||
No fixture data found for 'bad_fixture2'. (File format may be invalid.)
|
||||
|
||||
# Loading a fixture file with invalid data without file extension
|
||||
>>> management.call_command('loaddata', 'bad_fixture2', verbosity=0)
|
||||
No fixture data found for 'bad_fixture2'. (File format may be invalid.)
|
||||
|
||||
>>> sys.stderr = savestderr
|
||||
|
||||
"""}
|
||||
|
120
tests/regressiontests/model_inheritance_regress/models.py
Normal file
120
tests/regressiontests/model_inheritance_regress/models.py
Normal file
@ -0,0 +1,120 @@
|
||||
"""
|
||||
Regression tests for Model inheritance behaviour.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
class Place(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
address = models.CharField(max_length=80)
|
||||
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the place" % self.name
|
||||
|
||||
class Restaurant(Place):
|
||||
serves_hot_dogs = models.BooleanField()
|
||||
serves_pizza = models.BooleanField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the restaurant" % self.name
|
||||
|
||||
class ItalianRestaurant(Restaurant):
|
||||
serves_gnocchi = models.BooleanField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the italian restaurant" % self.name
|
||||
|
||||
class ParkingLot(Place):
|
||||
# An explicit link to the parent (we can control the attribute name).
|
||||
parent = models.OneToOneField(Place, primary_key=True, parent_link=True)
|
||||
capacity = models.IntegerField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the parking lot" % self.name
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Regression for #7350, #7202
|
||||
# Check that when you create a Parent object with a specific reference to an existent
|
||||
# child instance, saving the Parent doesn't duplicate the child.
|
||||
# This behaviour is only activated during a raw save - it is mostly relevant to
|
||||
# deserialization, but any sort of CORBA style 'narrow()' API would require a
|
||||
# similar approach.
|
||||
|
||||
# Create a child-parent-grandparent chain
|
||||
>>> place1 = Place(name="Guido's House of Pasta", address='944 W. Fullerton')
|
||||
>>> place1.save_base(raw=True)
|
||||
>>> restaurant = Restaurant(place_ptr=place1, serves_hot_dogs=True, serves_pizza=False)
|
||||
>>> restaurant.save_base(raw=True)
|
||||
>>> italian_restaurant = ItalianRestaurant(restaurant_ptr=restaurant, serves_gnocchi=True)
|
||||
>>> italian_restaurant.save_base(raw=True)
|
||||
|
||||
# Create a child-parent chain with an explicit parent link
|
||||
>>> place2 = Place(name='Main St', address='111 Main St')
|
||||
>>> place2.save_base(raw=True)
|
||||
>>> park = ParkingLot(parent=place2, capacity=100)
|
||||
>>> park.save_base(raw=True)
|
||||
|
||||
# Check that no extra parent objects have been created.
|
||||
>>> Place.objects.all()
|
||||
[<Place: Guido's House of Pasta the place>, <Place: Main St the place>]
|
||||
|
||||
>>> dicts = Restaurant.objects.values('name','serves_hot_dogs')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's House of Pasta"), ('serves_hot_dogs', True)]]
|
||||
|
||||
>>> dicts = ItalianRestaurant.objects.values('name','serves_hot_dogs','serves_gnocchi')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's House of Pasta"), ('serves_gnocchi', True), ('serves_hot_dogs', True)]]
|
||||
|
||||
>>> dicts = ParkingLot.objects.values('name','capacity')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('capacity', 100), ('name', u'Main St')]]
|
||||
|
||||
# You can also update objects when using a raw save.
|
||||
>>> place1.name = "Guido's All New House of Pasta"
|
||||
>>> place1.save_base(raw=True)
|
||||
|
||||
>>> restaurant.serves_hot_dogs = False
|
||||
>>> restaurant.save_base(raw=True)
|
||||
|
||||
>>> italian_restaurant.serves_gnocchi = False
|
||||
>>> italian_restaurant.save_base(raw=True)
|
||||
|
||||
>>> place2.name='Derelict lot'
|
||||
>>> place2.save_base(raw=True)
|
||||
|
||||
>>> park.capacity = 50
|
||||
>>> park.save_base(raw=True)
|
||||
|
||||
# No extra parent objects after an update, either.
|
||||
>>> Place.objects.all()
|
||||
[<Place: Derelict lot the place>, <Place: Guido's All New House of Pasta the place>]
|
||||
|
||||
>>> dicts = Restaurant.objects.values('name','serves_hot_dogs')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's All New House of Pasta"), ('serves_hot_dogs', False)]]
|
||||
|
||||
>>> dicts = ItalianRestaurant.objects.values('name','serves_hot_dogs','serves_gnocchi')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's All New House of Pasta"), ('serves_gnocchi', False), ('serves_hot_dogs', False)]]
|
||||
|
||||
>>> dicts = ParkingLot.objects.values('name','capacity')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('capacity', 50), ('name', u'Derelict lot')]]
|
||||
|
||||
# If you try to raw_save a parent attribute onto a child object,
|
||||
# the attribute will be ignored.
|
||||
|
||||
>>> italian_restaurant.name = "Lorenzo's Pasta Hut"
|
||||
>>> italian_restaurant.save_base(raw=True)
|
||||
|
||||
# Note that the name has not changed
|
||||
# - name is an attribute of Place, not ItalianRestaurant
|
||||
>>> dicts = ItalianRestaurant.objects.values('name','serves_hot_dogs','serves_gnocchi')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's All New House of Pasta"), ('serves_gnocchi', False), ('serves_hot_dogs', False)]]
|
||||
|
||||
"""}
|
0
tests/regressiontests/null_fk/__init__.py
Normal file
0
tests/regressiontests/null_fk/__init__.py
Normal file
55
tests/regressiontests/null_fk/models.py
Normal file
55
tests/regressiontests/null_fk/models.py
Normal file
@ -0,0 +1,55 @@
|
||||
"""
|
||||
Regression tests for proper working of ForeignKey(null=True). Tests these bugs:
|
||||
|
||||
* #7369: FK non-null after null relationship on select_related() generates an invalid query
|
||||
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
class SystemInfo(models.Model):
|
||||
system_name = models.CharField(max_length=32)
|
||||
|
||||
class Forum(models.Model):
|
||||
system_info = models.ForeignKey(SystemInfo)
|
||||
forum_name = models.CharField(max_length=32)
|
||||
|
||||
class Post(models.Model):
|
||||
forum = models.ForeignKey(Forum, null=True)
|
||||
title = models.CharField(max_length=32)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
class Comment(models.Model):
|
||||
post = models.ForeignKey(Post, null=True)
|
||||
comment_text = models.CharField(max_length=250)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.comment_text
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
|
||||
>>> s = SystemInfo.objects.create(system_name='First forum')
|
||||
>>> f = Forum.objects.create(system_info=s, forum_name='First forum')
|
||||
>>> p = Post.objects.create(forum=f, title='First Post')
|
||||
>>> c1 = Comment.objects.create(post=p, comment_text='My first comment')
|
||||
>>> c2 = Comment.objects.create(comment_text='My second comment')
|
||||
|
||||
# Starting from comment, make sure that a .select_related(...) with a specified
|
||||
# set of fields will properly LEFT JOIN multiple levels of NULLs (and the things
|
||||
# that come after the NULLs, or else data that should exist won't).
|
||||
>>> c = Comment.objects.select_related().get(id=1)
|
||||
>>> c.post
|
||||
<Post: First Post>
|
||||
>>> c = Comment.objects.select_related().get(id=2)
|
||||
>>> print c.post
|
||||
None
|
||||
|
||||
>>> comments = Comment.objects.select_related('post__forum__system_info').all()
|
||||
>>> [(c.id, c.post.id) for c in comments]
|
||||
[(1, 1), (2, None)]
|
||||
>>> [(c.comment_text, c.post.title) for c in comments]
|
||||
[(u'My first comment', u'First Post'), (u'My second comment', None)]
|
||||
|
||||
"""}
|
@ -223,3 +223,23 @@ class ModifyingSaveData(models.Model):
|
||||
"A save method that modifies the data in the object"
|
||||
self.data = 666
|
||||
super(ModifyingSaveData, self).save(raw)
|
||||
|
||||
# Tests for serialization of models using inheritance.
|
||||
# Regression for #7202, #7350
|
||||
class AbstractBaseModel(models.Model):
|
||||
parent_data = models.IntegerField()
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class InheritAbstractModel(AbstractBaseModel):
|
||||
child_data = models.IntegerField()
|
||||
|
||||
class BaseModel(models.Model):
|
||||
parent_data = models.IntegerField()
|
||||
|
||||
class InheritBaseModel(BaseModel):
|
||||
child_data = models.IntegerField()
|
||||
|
||||
class ExplicitInheritBaseModel(BaseModel):
|
||||
parent = models.OneToOneField(BaseModel)
|
||||
child_data = models.IntegerField()
|
||||
|
@ -32,7 +32,7 @@ def data_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
instance.data = data
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def generic_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
@ -40,31 +40,44 @@ def generic_create(pk, klass, data):
|
||||
models.Model.save_base(instance, raw=True)
|
||||
for tag in data[1:]:
|
||||
instance.tags.create(data=tag)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def fk_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
setattr(instance, 'data_id', data)
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def m2m_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
models.Model.save_base(instance, raw=True)
|
||||
instance.data = data
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def o2o_create(pk, klass, data):
|
||||
instance = klass()
|
||||
instance.data_id = data
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def pk_create(pk, klass, data):
|
||||
instance = klass()
|
||||
instance.data = data
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def inherited_create(pk, klass, data):
|
||||
instance = klass(id=pk,**data)
|
||||
# This isn't a raw save because:
|
||||
# 1) we're testing inheritance, not field behaviour, so none
|
||||
# of the field values need to be protected.
|
||||
# 2) saving the child class and having the parent created
|
||||
# automatically is easier than manually creating both.
|
||||
models.Model.save(instance)
|
||||
created = [instance]
|
||||
for klass,field in instance._meta.parents.items():
|
||||
created.append(klass.objects.get(id=pk))
|
||||
return created
|
||||
|
||||
# A set of functions that can be used to compare
|
||||
# test data objects of various kinds
|
||||
@ -94,6 +107,11 @@ def pk_compare(testcase, pk, klass, data):
|
||||
instance = klass.objects.get(data=data)
|
||||
testcase.assertEqual(data, instance.data)
|
||||
|
||||
def inherited_compare(testcase, pk, klass, data):
|
||||
instance = klass.objects.get(id=pk)
|
||||
for key,value in data.items():
|
||||
testcase.assertEqual(value, getattr(instance,key))
|
||||
|
||||
# Define some data types. Each data type is
|
||||
# actually a pair of functions; one to create
|
||||
# and one to compare objects of that type
|
||||
@ -103,6 +121,7 @@ fk_obj = (fk_create, fk_compare)
|
||||
m2m_obj = (m2m_create, m2m_compare)
|
||||
o2o_obj = (o2o_create, o2o_compare)
|
||||
pk_obj = (pk_create, pk_compare)
|
||||
inherited_obj = (inherited_create, inherited_compare)
|
||||
|
||||
test_data = [
|
||||
# Format: (data type, PK value, Model Class, data)
|
||||
@ -255,6 +274,10 @@ The end."""),
|
||||
|
||||
(data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006,6,16,10,42,37)),
|
||||
(data_obj, 810, ModifyingSaveData, 42),
|
||||
|
||||
(inherited_obj, 900, InheritAbstractModel, {'child_data':37,'parent_data':42}),
|
||||
(inherited_obj, 910, ExplicitInheritBaseModel, {'child_data':37,'parent_data':42}),
|
||||
(inherited_obj, 920, InheritBaseModel, {'child_data':37,'parent_data':42}),
|
||||
]
|
||||
|
||||
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
||||
@ -277,13 +300,19 @@ def serializerTest(format, self):
|
||||
|
||||
# Create all the objects defined in the test data
|
||||
objects = []
|
||||
instance_count = {}
|
||||
transaction.enter_transaction_management()
|
||||
transaction.managed(True)
|
||||
for (func, pk, klass, datum) in test_data:
|
||||
objects.append(func[0](pk, klass, datum))
|
||||
objects.extend(func[0](pk, klass, datum))
|
||||
instance_count[klass] = 0
|
||||
transaction.commit()
|
||||
transaction.leave_transaction_management()
|
||||
|
||||
# Get a count of the number of objects created for each class
|
||||
for klass in instance_count:
|
||||
instance_count[klass] = klass.objects.count()
|
||||
|
||||
# Add the generic tagged objects to the object list
|
||||
objects.extend(Tag.objects.all())
|
||||
|
||||
@ -304,6 +333,11 @@ def serializerTest(format, self):
|
||||
for (func, pk, klass, datum) in test_data:
|
||||
func[1](self, pk, klass, datum)
|
||||
|
||||
# Assert that the number of objects deserialized is the
|
||||
# same as the number that was serialized.
|
||||
for klass, count in instance_count.items():
|
||||
self.assertEquals(count, klass.objects.count())
|
||||
|
||||
def fieldsTest(format, self):
|
||||
# Clear the database first
|
||||
management.call_command('flush', verbosity=0, interactive=False)
|
||||
|
Loading…
x
Reference in New Issue
Block a user