mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +00:00
Merge trunk to [3226]
git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@3228 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
54b6e96957
commit
da5b0586d3
@ -281,3 +281,9 @@ COMMENTS_FIRST_FEW = 0
|
|||||||
# A tuple of IP addresses that have been banned from participating in various
|
# A tuple of IP addresses that have been banned from participating in various
|
||||||
# Django-powered features.
|
# Django-powered features.
|
||||||
BANNED_IPS = ()
|
BANNED_IPS = ()
|
||||||
|
|
||||||
|
##################
|
||||||
|
# AUTHENTICATION #
|
||||||
|
##################
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django import http, template
|
from django import http, template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User, SESSION_KEY
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth import authenticate, login
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.utils.translation import gettext_lazy
|
from django.utils.translation import gettext_lazy
|
||||||
import base64, datetime, md5
|
import base64, datetime, md5
|
||||||
@ -69,10 +70,10 @@ def staff_member_required(view_func):
|
|||||||
return _display_login_form(request, message)
|
return _display_login_form(request, message)
|
||||||
|
|
||||||
# Check the password.
|
# Check the password.
|
||||||
username = request.POST.get('username', '')
|
username = request.POST.get('username', None)
|
||||||
try:
|
password = request.POST.get('password', None)
|
||||||
user = User.objects.get(username=username, is_staff=True)
|
user = authenticate(username=username, password=password)
|
||||||
except User.DoesNotExist:
|
if user is None:
|
||||||
message = ERROR_MESSAGE
|
message = ERROR_MESSAGE
|
||||||
if '@' in username:
|
if '@' in username:
|
||||||
# Mistakenly entered e-mail address instead of username? Look it up.
|
# Mistakenly entered e-mail address instead of username? Look it up.
|
||||||
@ -86,8 +87,9 @@ def staff_member_required(view_func):
|
|||||||
|
|
||||||
# The user data is correct; log in the user in and continue.
|
# The user data is correct; log in the user in and continue.
|
||||||
else:
|
else:
|
||||||
if user.check_password(request.POST.get('password', '')):
|
if user.is_staff:
|
||||||
request.session[SESSION_KEY] = user.id
|
login(request, user)
|
||||||
|
# TODO: set last_login with an event.
|
||||||
user.last_login = datetime.datetime.now()
|
user.last_login = datetime.datetime.now()
|
||||||
user.save()
|
user.save()
|
||||||
if request.POST.has_key('post_data'):
|
if request.POST.has_key('post_data'):
|
||||||
|
@ -1,2 +1,71 @@
|
|||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
SESSION_KEY = '_auth_user_id'
|
||||||
|
BACKEND_SESSION_KEY = '_auth_user_backend'
|
||||||
LOGIN_URL = '/accounts/login/'
|
LOGIN_URL = '/accounts/login/'
|
||||||
REDIRECT_FIELD_NAME = 'next'
|
REDIRECT_FIELD_NAME = 'next'
|
||||||
|
|
||||||
|
def load_backend(path):
|
||||||
|
i = path.rfind('.')
|
||||||
|
module, attr = path[:i], path[i+1:]
|
||||||
|
try:
|
||||||
|
mod = __import__(module, '', '', [attr])
|
||||||
|
except ImportError, e:
|
||||||
|
raise ImproperlyConfigured, 'Error importing authentication backend %s: "%s"' % (module, e)
|
||||||
|
try:
|
||||||
|
cls = getattr(mod, attr)
|
||||||
|
except AttributeError:
|
||||||
|
raise ImproperlyConfigured, 'Module "%s" does not define a "%s" authentication backend' % (module, attr)
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def get_backends():
|
||||||
|
from django.conf import settings
|
||||||
|
backends = []
|
||||||
|
for backend_path in settings.AUTHENTICATION_BACKENDS:
|
||||||
|
backends.append(load_backend(backend_path))
|
||||||
|
return backends
|
||||||
|
|
||||||
|
def authenticate(**credentials):
|
||||||
|
"""
|
||||||
|
If the given credentials, return a user object.
|
||||||
|
"""
|
||||||
|
for backend in get_backends():
|
||||||
|
try:
|
||||||
|
user = backend.authenticate(**credentials)
|
||||||
|
except TypeError:
|
||||||
|
# this backend doesn't accept these credentials as arguments, try the next one.
|
||||||
|
continue
|
||||||
|
if user is None:
|
||||||
|
continue
|
||||||
|
# annotate the user object with the path of the backend
|
||||||
|
user.backend = str(backend.__class__)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def login(request, user):
|
||||||
|
"""
|
||||||
|
Persist a user id and a backend in the request. This way a user doesn't
|
||||||
|
have to reauthenticate on every request.
|
||||||
|
"""
|
||||||
|
if user is None:
|
||||||
|
user = request.user
|
||||||
|
# TODO: It would be nice to support different login methods, like signed cookies.
|
||||||
|
request.session[SESSION_KEY] = user.id
|
||||||
|
request.session[BACKEND_SESSION_KEY] = user.backend
|
||||||
|
|
||||||
|
def logout(request):
|
||||||
|
"""
|
||||||
|
Remove the authenticated user's id from request.
|
||||||
|
"""
|
||||||
|
del request.session[SESSION_KEY]
|
||||||
|
del request.session[BACKEND_SESSION_KEY]
|
||||||
|
|
||||||
|
def get_user(request):
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
try:
|
||||||
|
user_id = request.session[SESSION_KEY]
|
||||||
|
backend_path = request.session[BACKEND_SESSION_KEY]
|
||||||
|
backend = load_backend(backend_path)
|
||||||
|
user = backend.get_user(user_id) or AnonymousUser()
|
||||||
|
except KeyError:
|
||||||
|
user = AnonymousUser()
|
||||||
|
return user
|
||||||
|
21
django/contrib/auth/backends.py
Normal file
21
django/contrib/auth/backends.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from django.contrib.auth.models import User, check_password
|
||||||
|
|
||||||
|
class ModelBackend:
|
||||||
|
"""
|
||||||
|
Authenticate against django.contrib.auth.models.User
|
||||||
|
"""
|
||||||
|
# TODO: Model, login attribute name and password attribute name should be
|
||||||
|
# configurable.
|
||||||
|
def authenticate(self, username=None, password=None):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(username=username)
|
||||||
|
if user.check_password(password):
|
||||||
|
return user
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_user(self, user_id):
|
||||||
|
try:
|
||||||
|
return User.objects.get(pk=user_id)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return None
|
@ -1,4 +1,5 @@
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.template import Context, loader
|
from django.template import Context, loader
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
@ -20,8 +21,7 @@ class AuthenticationForm(forms.Manipulator):
|
|||||||
self.fields = [
|
self.fields = [
|
||||||
forms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
forms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
||||||
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
|
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
|
||||||
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True,
|
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
|
||||||
validator_list=[self.isValidPasswordForUser]),
|
|
||||||
]
|
]
|
||||||
self.user_cache = None
|
self.user_cache = None
|
||||||
|
|
||||||
@ -30,16 +30,10 @@ class AuthenticationForm(forms.Manipulator):
|
|||||||
raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
|
raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
|
||||||
|
|
||||||
def isValidUser(self, field_data, all_data):
|
def isValidUser(self, field_data, all_data):
|
||||||
try:
|
username = field_data
|
||||||
self.user_cache = User.objects.get(username=field_data)
|
password = all_data.get('password', None)
|
||||||
except User.DoesNotExist:
|
self.user_cache = authenticate(username=username, password=password)
|
||||||
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
|
|
||||||
|
|
||||||
def isValidPasswordForUser(self, field_data, all_data):
|
|
||||||
if self.user_cache is None:
|
if self.user_cache is None:
|
||||||
return
|
|
||||||
if not self.user_cache.check_password(field_data):
|
|
||||||
self.user_cache = None
|
|
||||||
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
|
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
|
||||||
elif not self.user_cache.is_active:
|
elif not self.user_cache.is_active:
|
||||||
raise validators.ValidationError, _("This account is inactive.")
|
raise validators.ValidationError, _("This account is inactive.")
|
||||||
|
@ -4,12 +4,8 @@ class LazyUser(object):
|
|||||||
|
|
||||||
def __get__(self, request, obj_type=None):
|
def __get__(self, request, obj_type=None):
|
||||||
if self._user is None:
|
if self._user is None:
|
||||||
from django.contrib.auth.models import User, AnonymousUser, SESSION_KEY
|
from django.contrib.auth import get_user
|
||||||
try:
|
self._user = get_user(request)
|
||||||
user_id = request.session[SESSION_KEY]
|
|
||||||
self._user = User.objects.get(pk=user_id)
|
|
||||||
except (KeyError, User.DoesNotExist):
|
|
||||||
self._user = AnonymousUser()
|
|
||||||
return self._user
|
return self._user
|
||||||
|
|
||||||
class AuthenticationMiddleware(object):
|
class AuthenticationMiddleware(object):
|
||||||
|
@ -4,7 +4,19 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
SESSION_KEY = '_auth_user_id'
|
def check_password(raw_password, enc_password):
|
||||||
|
"""
|
||||||
|
Returns a boolean of whether the raw_password was correct. Handles
|
||||||
|
encryption formats behind the scenes.
|
||||||
|
"""
|
||||||
|
algo, salt, hsh = enc_password.split('$')
|
||||||
|
if algo == 'md5':
|
||||||
|
import md5
|
||||||
|
return hsh == md5.new(salt+raw_password).hexdigest()
|
||||||
|
elif algo == 'sha1':
|
||||||
|
import sha
|
||||||
|
return hsh == sha.new(salt+raw_password).hexdigest()
|
||||||
|
raise ValueError, "Got unknown password algorithm type in password."
|
||||||
|
|
||||||
class SiteProfileNotAvailable(Exception):
|
class SiteProfileNotAvailable(Exception):
|
||||||
pass
|
pass
|
||||||
@ -141,14 +153,7 @@ class User(models.Model):
|
|||||||
self.set_password(raw_password)
|
self.set_password(raw_password)
|
||||||
self.save()
|
self.save()
|
||||||
return is_correct
|
return is_correct
|
||||||
algo, salt, hsh = self.password.split('$')
|
return check_password(raw_password, self.password)
|
||||||
if algo == 'md5':
|
|
||||||
import md5
|
|
||||||
return hsh == md5.new(salt+raw_password).hexdigest()
|
|
||||||
elif algo == 'sha1':
|
|
||||||
import sha
|
|
||||||
return hsh == sha.new(salt+raw_password).hexdigest()
|
|
||||||
raise ValueError, "Got unknown password algorithm type in password."
|
|
||||||
|
|
||||||
def get_group_permissions(self):
|
def get_group_permissions(self):
|
||||||
"Returns a list of permission strings that this user has through his/her groups."
|
"Returns a list of permission strings that this user has through his/her groups."
|
||||||
|
@ -3,7 +3,6 @@ from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.contrib.auth.models import SESSION_KEY
|
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
@ -19,7 +18,8 @@ def login(request, template_name='registration/login.html'):
|
|||||||
# Light security check -- make sure redirect_to isn't garbage.
|
# Light security check -- make sure redirect_to isn't garbage.
|
||||||
if not redirect_to or '://' in redirect_to or ' ' in redirect_to:
|
if not redirect_to or '://' in redirect_to or ' ' in redirect_to:
|
||||||
redirect_to = '/accounts/profile/'
|
redirect_to = '/accounts/profile/'
|
||||||
request.session[SESSION_KEY] = manipulator.get_user_id()
|
from django.contrib.auth import login
|
||||||
|
login(request, manipulator.get_user())
|
||||||
request.session.delete_test_cookie()
|
request.session.delete_test_cookie()
|
||||||
return HttpResponseRedirect(redirect_to)
|
return HttpResponseRedirect(redirect_to)
|
||||||
else:
|
else:
|
||||||
@ -33,8 +33,9 @@ def login(request, template_name='registration/login.html'):
|
|||||||
|
|
||||||
def logout(request, next_page=None, template_name='registration/logged_out.html'):
|
def logout(request, next_page=None, template_name='registration/logged_out.html'):
|
||||||
"Logs out the user and displays 'You are logged out' message."
|
"Logs out the user and displays 'You are logged out' message."
|
||||||
|
from django.contrib.auth import logout
|
||||||
try:
|
try:
|
||||||
del request.session[SESSION_KEY]
|
logout(request)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request))
|
return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request))
|
||||||
else:
|
else:
|
||||||
|
@ -5,7 +5,6 @@ from django.http import Http404
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.contrib.auth.models import SESSION_KEY
|
|
||||||
from django.contrib.comments.models import Comment, FreeComment, PHOTOS_REQUIRED, PHOTOS_OPTIONAL, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC
|
from django.contrib.comments.models import Comment, FreeComment, PHOTOS_REQUIRED, PHOTOS_OPTIONAL, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
@ -219,7 +218,8 @@ def post_comment(request):
|
|||||||
# If user gave correct username/password and wasn't already logged in, log them in
|
# If user gave correct username/password and wasn't already logged in, log them in
|
||||||
# so they don't have to enter a username/password again.
|
# so they don't have to enter a username/password again.
|
||||||
if manipulator.get_user() and new_data.has_key('password') and manipulator.get_user().check_password(new_data['password']):
|
if manipulator.get_user() and new_data.has_key('password') and manipulator.get_user().check_password(new_data['password']):
|
||||||
request.session[SESSION_KEY] = manipulator.get_user_id()
|
from django.contrib.auth import login
|
||||||
|
login(request, manipulator.get_user())
|
||||||
if errors or request.POST.has_key('preview'):
|
if errors or request.POST.has_key('preview'):
|
||||||
class CommentFormWrapper(forms.FormWrapper):
|
class CommentFormWrapper(forms.FormWrapper):
|
||||||
def __init__(self, manipulator, new_data, errors, rating_choices):
|
def __init__(self, manipulator, new_data, errors, rating_choices):
|
||||||
|
@ -27,7 +27,7 @@ def textile(value):
|
|||||||
raise template.TemplateSyntaxError, "Error in {% textile %} filter: The Python textile library isn't installed."
|
raise template.TemplateSyntaxError, "Error in {% textile %} filter: The Python textile library isn't installed."
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return textile.textile(value)
|
return textile.textile(value, encoding=settings.DEFAULT_CHARSET, output=settings.DEFAULT_CHARSET)
|
||||||
|
|
||||||
def markdown(value):
|
def markdown(value):
|
||||||
try:
|
try:
|
||||||
|
76
django/core/serializers/__init__.py
Normal file
76
django/core/serializers/__init__.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Interfaces for serializing Django objects.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
>>> from django.core import serializers
|
||||||
|
>>> json = serializers.serialize("json", some_query_set)
|
||||||
|
>>> objects = list(serializers.deserialize("json", json))
|
||||||
|
|
||||||
|
To add your own serializers, use the SERIALIZATION_MODULES setting::
|
||||||
|
|
||||||
|
SERIALIZATION_MODULES = {
|
||||||
|
"csv" : "path.to.csv.serializer",
|
||||||
|
"txt" : "path.to.txt.serializer",
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
# Built-in serializers
|
||||||
|
BUILTIN_SERIALIZERS = {
|
||||||
|
"xml" : "django.core.serializers.xml_serializer",
|
||||||
|
}
|
||||||
|
|
||||||
|
_serializers = {}
|
||||||
|
|
||||||
|
def register_serializer(format, serializer_module):
|
||||||
|
"""Register a new serializer by passing in a module name."""
|
||||||
|
module = __import__(serializer_module, '', '', [''])
|
||||||
|
_serializers[format] = module
|
||||||
|
|
||||||
|
def unregister_serializer(format):
|
||||||
|
"""Unregister a given serializer"""
|
||||||
|
del _serializers[format]
|
||||||
|
|
||||||
|
def get_serializer(format):
|
||||||
|
if not _serializers:
|
||||||
|
_load_serializers()
|
||||||
|
return _serializers[format].Serializer
|
||||||
|
|
||||||
|
def get_deserializer(format):
|
||||||
|
if not _serializers:
|
||||||
|
_load_serializers()
|
||||||
|
return _serializers[format].Deserializer
|
||||||
|
|
||||||
|
def serialize(format, queryset, **options):
|
||||||
|
"""
|
||||||
|
Serialize a queryset (or any iterator that returns database objects) using
|
||||||
|
a certain serializer.
|
||||||
|
"""
|
||||||
|
s = get_serializer(format)()
|
||||||
|
s.serialize(queryset, **options)
|
||||||
|
return s.getvalue()
|
||||||
|
|
||||||
|
def deserialize(format, stream_or_string):
|
||||||
|
"""
|
||||||
|
Deserialize a stream or a string. Returns an iterator that yields ``(obj,
|
||||||
|
m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* --
|
||||||
|
object, and ``m2m_relation_dict`` is a dictionary of ``{m2m_field_name :
|
||||||
|
list_of_related_objects}``.
|
||||||
|
"""
|
||||||
|
d = get_deserializer(format)
|
||||||
|
return d(stream_or_string)
|
||||||
|
|
||||||
|
def _load_serializers():
|
||||||
|
"""
|
||||||
|
Register built-in and settings-defined serializers. This is done lazily so
|
||||||
|
that user code has a chance to (e.g.) set up custom settings without
|
||||||
|
needing to be careful of import order.
|
||||||
|
"""
|
||||||
|
for format in BUILTIN_SERIALIZERS:
|
||||||
|
register_serializer(format, BUILTIN_SERIALIZERS[format])
|
||||||
|
if hasattr(settings, "SERIALIZATION_MODULES"):
|
||||||
|
for format in settings.SERIALIZATION_MODULES:
|
||||||
|
register_serializer(format, settings.SERIALIZATION_MODULES[format])
|
159
django/core/serializers/base.py
Normal file
159
django/core/serializers/base.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
"""
|
||||||
|
Module for abstract serializer/unserializer base classes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from StringIO import StringIO
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class SerializationError(Exception):
|
||||||
|
"""Something bad happened during serialization."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DeserializationError(Exception):
|
||||||
|
"""Something bad happened during deserialization."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Serializer(object):
|
||||||
|
"""
|
||||||
|
Abstract serializer base class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def serialize(self, queryset, **options):
|
||||||
|
"""
|
||||||
|
Serialize a queryset.
|
||||||
|
"""
|
||||||
|
self.options = options
|
||||||
|
|
||||||
|
self.stream = options.get("stream", StringIO())
|
||||||
|
|
||||||
|
self.start_serialization()
|
||||||
|
for obj in queryset:
|
||||||
|
self.start_object(obj)
|
||||||
|
for field in obj._meta.fields:
|
||||||
|
if field.rel is None:
|
||||||
|
self.handle_field(obj, field)
|
||||||
|
else:
|
||||||
|
self.handle_fk_field(obj, field)
|
||||||
|
for field in obj._meta.many_to_many:
|
||||||
|
self.handle_m2m_field(obj, field)
|
||||||
|
self.end_object(obj)
|
||||||
|
self.end_serialization()
|
||||||
|
return self.getvalue()
|
||||||
|
|
||||||
|
def get_string_value(self, obj, field):
|
||||||
|
"""
|
||||||
|
Convert a field's value to a string.
|
||||||
|
"""
|
||||||
|
if isinstance(field, models.DateTimeField):
|
||||||
|
value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
elif isinstance(field, models.FileField):
|
||||||
|
value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
|
||||||
|
else:
|
||||||
|
value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
def start_serialization(self):
|
||||||
|
"""
|
||||||
|
Called when serializing of the queryset starts.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def end_serialization(self):
|
||||||
|
"""
|
||||||
|
Called when serializing of the queryset ends.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def start_object(self, obj):
|
||||||
|
"""
|
||||||
|
Called when serializing of an object starts.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def end_object(self, obj):
|
||||||
|
"""
|
||||||
|
Called when serializing of an object ends.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_field(self, obj, field):
|
||||||
|
"""
|
||||||
|
Called to handle each individual (non-relational) field on an object.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def handle_fk_field(self, obj, field):
|
||||||
|
"""
|
||||||
|
Called to handle a ForeignKey field.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def handle_m2m_field(self, obj, field):
|
||||||
|
"""
|
||||||
|
Called to handle a ManyToManyField.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def getvalue(self):
|
||||||
|
"""
|
||||||
|
Return the fully serialized queryset.
|
||||||
|
"""
|
||||||
|
return self.stream.getvalue()
|
||||||
|
|
||||||
|
class Deserializer(object):
|
||||||
|
"""
|
||||||
|
Abstract base deserializer class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, stream_or_string, **options):
|
||||||
|
"""
|
||||||
|
Init this serializer given a stream or a string
|
||||||
|
"""
|
||||||
|
self.options = options
|
||||||
|
if isinstance(stream_or_string, basestring):
|
||||||
|
self.stream = StringIO(stream_or_string)
|
||||||
|
else:
|
||||||
|
self.stream = stream_or_string
|
||||||
|
# hack to make sure that the models have all been loaded before
|
||||||
|
# deserialization starts (otherwise subclass calls to get_model()
|
||||||
|
# and friends might fail...)
|
||||||
|
models.get_apps()
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
"""Iteration iterface -- return the next item in the stream"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class DeserializedObject(object):
|
||||||
|
"""
|
||||||
|
A deserialzed model.
|
||||||
|
|
||||||
|
Basically a container for holding the pre-saved deserialized data along
|
||||||
|
with the many-to-many data saved with the object.
|
||||||
|
|
||||||
|
Call ``save()`` to save the object (with the many-to-many data) to the
|
||||||
|
database; call ``save(save_m2m=False)`` to save just the object fields
|
||||||
|
(and not touch the many-to-many stuff.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, obj, m2m_data=None):
|
||||||
|
self.object = obj
|
||||||
|
self.m2m_data = m2m_data
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<DeserializedObject: %s>" % str(self.object)
|
||||||
|
|
||||||
|
def save(self, save_m2m=True):
|
||||||
|
self.object.save()
|
||||||
|
if self.m2m_data and save_m2m:
|
||||||
|
for accessor_name, object_list in self.m2m_data.items():
|
||||||
|
setattr(self.object, accessor_name, object_list)
|
||||||
|
|
||||||
|
# prevent a second (possibly accidental) call to save() from saving
|
||||||
|
# the m2m data twice.
|
||||||
|
self.m2m_data = None
|
218
django/core/serializers/xml_serializer.py
Normal file
218
django/core/serializers/xml_serializer.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
"""
|
||||||
|
XML serializer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from xml.dom import pulldom
|
||||||
|
from django.utils.xmlutils import SimplerXMLGenerator
|
||||||
|
from django.core.serializers import base
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Serializer(base.Serializer):
|
||||||
|
"""
|
||||||
|
Serializes a QuerySet to XML.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def start_serialization(self):
|
||||||
|
"""
|
||||||
|
Start serialization -- open the XML document and the root element.
|
||||||
|
"""
|
||||||
|
self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", "utf-8"))
|
||||||
|
self.xml.startDocument()
|
||||||
|
self.xml.startElement("django-objects", {"version" : "1.0"})
|
||||||
|
|
||||||
|
def end_serialization(self):
|
||||||
|
"""
|
||||||
|
End serialization -- end the document.
|
||||||
|
"""
|
||||||
|
self.xml.endElement("django-objects")
|
||||||
|
self.xml.endDocument()
|
||||||
|
|
||||||
|
def start_object(self, obj):
|
||||||
|
"""
|
||||||
|
Called as each object is handled.
|
||||||
|
"""
|
||||||
|
if not hasattr(obj, "_meta"):
|
||||||
|
raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
|
||||||
|
|
||||||
|
self.xml.startElement("object", {
|
||||||
|
"pk" : str(obj._get_pk_val()),
|
||||||
|
"model" : str(obj._meta),
|
||||||
|
})
|
||||||
|
|
||||||
|
def end_object(self, obj):
|
||||||
|
"""
|
||||||
|
Called after handling all fields for an object.
|
||||||
|
"""
|
||||||
|
self.xml.endElement("object")
|
||||||
|
|
||||||
|
def handle_field(self, obj, field):
|
||||||
|
"""
|
||||||
|
Called to handle each field on an object (except for ForeignKeys and
|
||||||
|
ManyToManyFields)
|
||||||
|
"""
|
||||||
|
self.xml.startElement("field", {
|
||||||
|
"name" : field.name,
|
||||||
|
"type" : field.get_internal_type()
|
||||||
|
})
|
||||||
|
|
||||||
|
# Get a "string version" of the object's data (this is handled by the
|
||||||
|
# serializer base class). None is handled specially.
|
||||||
|
value = self.get_string_value(obj, field)
|
||||||
|
if value is None:
|
||||||
|
self.xml.addQuickElement("None")
|
||||||
|
else:
|
||||||
|
self.xml.characters(str(value))
|
||||||
|
|
||||||
|
self.xml.endElement("field")
|
||||||
|
|
||||||
|
def handle_fk_field(self, obj, field):
|
||||||
|
"""
|
||||||
|
Called to handle a ForeignKey (we need to treat them slightly
|
||||||
|
differently from regular fields).
|
||||||
|
"""
|
||||||
|
self._start_relational_field(field)
|
||||||
|
related = getattr(obj, field.name)
|
||||||
|
if related is not None:
|
||||||
|
self.xml.characters(str(related._get_pk_val()))
|
||||||
|
else:
|
||||||
|
self.xml.addQuickElement("None")
|
||||||
|
self.xml.endElement("field")
|
||||||
|
|
||||||
|
def handle_m2m_field(self, obj, field):
|
||||||
|
"""
|
||||||
|
Called to handle a ManyToManyField. Related objects are only
|
||||||
|
serialized as references to the object's PK (i.e. the related *data*
|
||||||
|
is not dumped, just the relation).
|
||||||
|
"""
|
||||||
|
self._start_relational_field(field)
|
||||||
|
for relobj in getattr(obj, field.name).iterator():
|
||||||
|
self.xml.addQuickElement("object", attrs={"pk" : str(relobj._get_pk_val())})
|
||||||
|
self.xml.endElement("field")
|
||||||
|
|
||||||
|
def _start_relational_field(self, field):
|
||||||
|
"""
|
||||||
|
Helper to output the <field> element for relational fields
|
||||||
|
"""
|
||||||
|
self.xml.startElement("field", {
|
||||||
|
"name" : field.name,
|
||||||
|
"rel" : field.rel.__class__.__name__,
|
||||||
|
"to" : str(field.rel.to._meta),
|
||||||
|
})
|
||||||
|
|
||||||
|
class Deserializer(base.Deserializer):
|
||||||
|
"""
|
||||||
|
Deserialize XML.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, stream_or_string, **options):
|
||||||
|
super(Deserializer, self).__init__(stream_or_string, **options)
|
||||||
|
self.encoding = self.options.get("encoding", "utf-8")
|
||||||
|
self.event_stream = pulldom.parse(self.stream)
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
for event, node in self.event_stream:
|
||||||
|
if event == "START_ELEMENT" and node.nodeName == "object":
|
||||||
|
self.event_stream.expandNode(node)
|
||||||
|
return self._handle_object(node)
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def _handle_object(self, node):
|
||||||
|
"""
|
||||||
|
Convert an <object> node to a DeserializedObject.
|
||||||
|
"""
|
||||||
|
# Look up the model using the model loading mechanism. If this fails, bail.
|
||||||
|
Model = self._get_model_from_node(node, "model")
|
||||||
|
|
||||||
|
# Start building a data dictionary from the object. If the node is
|
||||||
|
# missing the pk attribute, bail.
|
||||||
|
pk = node.getAttribute("pk")
|
||||||
|
if not pk:
|
||||||
|
raise base.DeserializationError("<object> node is missing the 'pk' attribute")
|
||||||
|
data = {Model._meta.pk.name : pk}
|
||||||
|
|
||||||
|
# Also start building a dict of m2m data (this is saved as
|
||||||
|
# {m2m_accessor_attribute : [list_of_related_objects]})
|
||||||
|
m2m_data = {}
|
||||||
|
|
||||||
|
# Deseralize each field.
|
||||||
|
for field_node in node.getElementsByTagName("field"):
|
||||||
|
# If the field is missing the name attribute, bail (are you
|
||||||
|
# sensing a pattern here?)
|
||||||
|
field_name = field_node.getAttribute("name")
|
||||||
|
if not field_name:
|
||||||
|
raise base.DeserializationError("<field> node is missing the 'name' attribute")
|
||||||
|
|
||||||
|
# Get the field from the Model. This will raise a
|
||||||
|
# FieldDoesNotExist if, well, the field doesn't exist, which will
|
||||||
|
# be propagated correctly.
|
||||||
|
field = Model._meta.get_field(field_name)
|
||||||
|
|
||||||
|
# As is usually the case, relation fields get the special treatment.
|
||||||
|
if field.rel and isinstance(field.rel, models.ManyToManyRel):
|
||||||
|
m2m_data[field.name] = self._handle_m2m_field_node(field_node)
|
||||||
|
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
|
||||||
|
data[field.name] = self._handle_fk_field_node(field_node)
|
||||||
|
else:
|
||||||
|
value = field.to_python(getInnerText(field_node).strip().encode(self.encoding))
|
||||||
|
data[field.name] = value
|
||||||
|
|
||||||
|
# Return a DeserializedObject so that the m2m data has a place to live.
|
||||||
|
return base.DeserializedObject(Model(**data), m2m_data)
|
||||||
|
|
||||||
|
def _handle_fk_field_node(self, node):
|
||||||
|
"""
|
||||||
|
Handle a <field> node for a ForeignKey
|
||||||
|
"""
|
||||||
|
# Try to set the foreign key by looking up the foreign related object.
|
||||||
|
# If it doesn't exist, set the field to None (which might trigger
|
||||||
|
# validation error, but that's expected).
|
||||||
|
RelatedModel = self._get_model_from_node(node, "to")
|
||||||
|
return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding))
|
||||||
|
|
||||||
|
def _handle_m2m_field_node(self, node):
|
||||||
|
"""
|
||||||
|
Handle a <field> node for a ManyToManyField
|
||||||
|
"""
|
||||||
|
# Load the related model
|
||||||
|
RelatedModel = self._get_model_from_node(node, "to")
|
||||||
|
|
||||||
|
# Look up all the related objects. Using the in_bulk() lookup ensures
|
||||||
|
# that missing related objects don't cause an exception
|
||||||
|
related_ids = [c.getAttribute("pk").encode(self.encoding) for c in node.getElementsByTagName("object")]
|
||||||
|
return RelatedModel._default_manager.in_bulk(related_ids).values()
|
||||||
|
|
||||||
|
def _get_model_from_node(self, node, attr):
|
||||||
|
"""
|
||||||
|
Helper to look up a model from a <object model=...> or a <field
|
||||||
|
rel=... to=...> node.
|
||||||
|
"""
|
||||||
|
model_identifier = node.getAttribute(attr)
|
||||||
|
if not model_identifier:
|
||||||
|
raise base.DeserializationError(
|
||||||
|
"<%s> node is missing the required '%s' attribute" \
|
||||||
|
% (node.nodeName, attr))
|
||||||
|
try:
|
||||||
|
Model = models.get_model(*model_identifier.split("."))
|
||||||
|
except TypeError:
|
||||||
|
Model = None
|
||||||
|
if Model is None:
|
||||||
|
raise base.DeserializationError(
|
||||||
|
"<%s> node has invalid model identifier: '%s'" % \
|
||||||
|
(node.nodeName, model_identifier))
|
||||||
|
return Model
|
||||||
|
|
||||||
|
|
||||||
|
def getInnerText(node):
|
||||||
|
"""
|
||||||
|
Get all the inner text of a DOM node (recursively).
|
||||||
|
"""
|
||||||
|
# inspired by http://mail.python.org/pipermail/xml-sig/2005-March/011022.html
|
||||||
|
inner_text = []
|
||||||
|
for child in node.childNodes:
|
||||||
|
if child.nodeType == child.TEXT_NODE or child.nodeType == child.CDATA_SECTION_NODE:
|
||||||
|
inner_text.append(child.data)
|
||||||
|
elif child.nodeType == child.ELEMENT_NODE:
|
||||||
|
inner_text.extend(getInnerText(child))
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return "".join(inner_text)
|
@ -399,10 +399,10 @@ def method_set_order(ordered_obj, self, id_list):
|
|||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
# Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
|
# Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
|
||||||
sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
|
sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
|
||||||
(backend.quote_name(ordered_obj.db_table), backend.quote_name('_order'),
|
(backend.quote_name(ordered_obj._meta.db_table), backend.quote_name('_order'),
|
||||||
backend.quote_name(ordered_obj.order_with_respect_to.column),
|
backend.quote_name(ordered_obj._meta.order_with_respect_to.column),
|
||||||
backend.quote_name(ordered_obj.pk.column))
|
backend.quote_name(ordered_obj._meta.pk.column))
|
||||||
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
|
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
|
||||||
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
|
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
|
@ -411,7 +411,7 @@ class DateField(Field):
|
|||||||
def get_db_prep_lookup(self, lookup_type, value):
|
def get_db_prep_lookup(self, lookup_type, value):
|
||||||
if lookup_type == 'range':
|
if lookup_type == 'range':
|
||||||
value = [str(v) for v in value]
|
value = [str(v) for v in value]
|
||||||
elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne'):
|
elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne') and hasattr(value, 'strftime'):
|
||||||
value = value.strftime('%Y-%m-%d')
|
value = value.strftime('%Y-%m-%d')
|
||||||
else:
|
else:
|
||||||
value = str(value)
|
value = str(value)
|
||||||
|
@ -32,18 +32,25 @@ def get_apps():
|
|||||||
_app_errors[app_name] = e
|
_app_errors[app_name] = e
|
||||||
return _app_list
|
return _app_list
|
||||||
|
|
||||||
def get_app(app_label):
|
def get_app(app_label, emptyOK = False):
|
||||||
"Returns the module containing the models for the given app_label."
|
"Returns the module containing the models for the given app_label. If the app has no models in it and 'emptyOK' is True, returns None."
|
||||||
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
||||||
for app_name in settings.INSTALLED_APPS:
|
for app_name in settings.INSTALLED_APPS:
|
||||||
if app_label == app_name.split('.')[-1]:
|
if app_label == app_name.split('.')[-1]:
|
||||||
return load_app(app_name)
|
mod = load_app(app_name)
|
||||||
|
if mod is None:
|
||||||
|
if emptyOK:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return mod
|
||||||
raise ImproperlyConfigured, "App with label %s could not be found" % app_label
|
raise ImproperlyConfigured, "App with label %s could not be found" % app_label
|
||||||
|
|
||||||
def load_app(app_name):
|
def load_app(app_name):
|
||||||
"Loads the app with the provided fully qualified name, and returns the model module."
|
"Loads the app with the provided fully qualified name, and returns the model module."
|
||||||
global _app_list
|
global _app_list
|
||||||
mod = __import__(app_name, '', '', ['models'])
|
mod = __import__(app_name, '', '', ['models'])
|
||||||
|
if not hasattr(mod, 'models'):
|
||||||
|
return None
|
||||||
if mod.models not in _app_list:
|
if mod.models not in _app_list:
|
||||||
_app_list.append(mod.models)
|
_app_list.append(mod.models)
|
||||||
return mod.models
|
return mod.models
|
||||||
|
@ -69,8 +69,11 @@ class Manager(object):
|
|||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
return self.get_query_set().get(*args, **kwargs)
|
return self.get_query_set().get(*args, **kwargs)
|
||||||
|
|
||||||
def get_or_create(self, *args, **kwargs):
|
def get_or_create(self, **kwargs):
|
||||||
return self.get_query_set().get_or_create(*args, **kwargs)
|
return self.get_query_set().get_or_create(**kwargs)
|
||||||
|
|
||||||
|
def create(self, **kwargs):
|
||||||
|
return self.get_query_set().create(**kwargs)
|
||||||
|
|
||||||
def filter(self, *args, **kwargs):
|
def filter(self, *args, **kwargs):
|
||||||
return self.get_query_set().filter(*args, **kwargs)
|
return self.get_query_set().filter(*args, **kwargs)
|
||||||
|
@ -204,6 +204,15 @@ class QuerySet(object):
|
|||||||
raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name
|
raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name
|
||||||
assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs)
|
assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs)
|
||||||
return obj_list[0]
|
return obj_list[0]
|
||||||
|
|
||||||
|
def create(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a new object with the given kwargs, saving it to the database
|
||||||
|
and returning the created object.
|
||||||
|
"""
|
||||||
|
obj = self.model(**kwargs)
|
||||||
|
obj.save()
|
||||||
|
return obj
|
||||||
|
|
||||||
def get_or_create(self, **kwargs):
|
def get_or_create(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -267,17 +267,25 @@ previous section). You can tell them apart with ``is_anonymous()``, like so::
|
|||||||
How to log a user in
|
How to log a user in
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
To log a user in, do the following within a view::
|
Depending on your task, you'll probably want to make sure to validate the
|
||||||
|
user's username and password before you log them in. The easiest way to do so
|
||||||
|
is to use the built-in ``authenticate`` and ``login`` functions from within a
|
||||||
|
view::
|
||||||
|
|
||||||
from django.contrib.auth.models import SESSION_KEY
|
from django.contrib.auth import authenticate, login
|
||||||
request.session[SESSION_KEY] = some_user.id
|
username = request.POST['username']
|
||||||
|
password = request.POST['password']
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
if user is not None:
|
||||||
|
login(request, user)
|
||||||
|
|
||||||
Because this uses sessions, you'll need to make sure you have
|
``authenticate`` checks the username and password. If they are valid it
|
||||||
``SessionMiddleware`` enabled. See the `session documentation`_ for more
|
returns a user object, otherwise it returns ``None``. ``login`` makes it so
|
||||||
information.
|
your users don't have send a username and password for every request. Because
|
||||||
|
the ``login`` function uses sessions, you'll need to make sure you have
|
||||||
|
``SessionMiddleware`` enabled. See the `session documentation`_ for
|
||||||
|
more information.
|
||||||
|
|
||||||
This assumes ``some_user`` is your ``User`` instance. Depending on your task,
|
|
||||||
you'll probably want to make sure to validate the user's username and password.
|
|
||||||
|
|
||||||
Limiting access to logged-in users
|
Limiting access to logged-in users
|
||||||
----------------------------------
|
----------------------------------
|
||||||
@ -672,3 +680,84 @@ Finally, note that this messages framework only works with users in the user
|
|||||||
database. To send messages to anonymous users, use the `session framework`_.
|
database. To send messages to anonymous users, use the `session framework`_.
|
||||||
|
|
||||||
.. _session framework: http://www.djangoproject.com/documentation/sessions/
|
.. _session framework: http://www.djangoproject.com/documentation/sessions/
|
||||||
|
|
||||||
|
Other Authentication Sources
|
||||||
|
============================
|
||||||
|
|
||||||
|
Django supports other authentication sources as well. You can even use
|
||||||
|
multiple sources at the same time.
|
||||||
|
|
||||||
|
Using multiple backends
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The list of backends to use is controlled by the ``AUTHENTICATION_BACKENDS``
|
||||||
|
setting. This should be a tuple of python path names. It defaults to
|
||||||
|
``('django.contrib.auth.backends.ModelBackend',)``. To add additional backends
|
||||||
|
just add them to your settings.py file. Ordering matters, so if the same
|
||||||
|
username and password is valid in multiple backends, the first one in the
|
||||||
|
list will return a user object, and the remaining ones won't even get a chance.
|
||||||
|
|
||||||
|
Writing an authentication backend
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
An authentication backend is a class that implements 2 methods:
|
||||||
|
``get_user(id)`` and ``authenticate(**credentials)``. The ``get_user`` method
|
||||||
|
takes an id, which could be a username, and database id, whatever, and returns
|
||||||
|
a user object. The ``authenticate`` method takes credentials as keyword
|
||||||
|
arguments. Many times it will just look like this::
|
||||||
|
|
||||||
|
class MyBackend:
|
||||||
|
def authenticate(username=None, password=None):
|
||||||
|
# check the username/password and return a user
|
||||||
|
|
||||||
|
but it could also authenticate a token like so::
|
||||||
|
|
||||||
|
class MyBackend:
|
||||||
|
def authenticate(token=None):
|
||||||
|
# check the token and return a user
|
||||||
|
|
||||||
|
Regardless, ``authenticate`` should check the credentials it gets, and if they
|
||||||
|
are valid, it should return a user object that matches those credentials.
|
||||||
|
|
||||||
|
The Django admin system is tightly coupled to the Django User object described
|
||||||
|
at the beginning of this document. For now, the best way to deal with this is
|
||||||
|
to create a Django User object for each user that exists for your backend
|
||||||
|
(i.e. in your LDAP directory, your external SQL database, etc.) You can either
|
||||||
|
write a script to do this in advance, or your ``authenticate`` method can do
|
||||||
|
it the first time a user logs in. Here's an example backend that
|
||||||
|
authenticates against a username and password variable defined in your
|
||||||
|
``settings.py`` file and creates a Django user object the first time they
|
||||||
|
authenticate::
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import User, check_password
|
||||||
|
|
||||||
|
class SettingsBackend:
|
||||||
|
"""
|
||||||
|
Authenticate against vars in settings.py Use the login name, and a hash
|
||||||
|
of the password. For example:
|
||||||
|
|
||||||
|
ADMIN_LOGIN = 'admin'
|
||||||
|
ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
|
||||||
|
"""
|
||||||
|
def authenticate(self, username=None, password=None):
|
||||||
|
login_valid = (settings.ADMIN_LOGIN == username)
|
||||||
|
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
|
||||||
|
if login_valid and pwd_valid:
|
||||||
|
try:
|
||||||
|
user = User.objects.get(username=username)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
# Create a new user. Note that we can set password to anything
|
||||||
|
# as it won't be checked, the password from settings.py will.
|
||||||
|
user = User(username=username, password='get from settings.py')
|
||||||
|
user.is_staff = True
|
||||||
|
user.is_superuser = True
|
||||||
|
user.save()
|
||||||
|
return user
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_user(self, user_id):
|
||||||
|
try:
|
||||||
|
return User.objects.get(pk=user_id)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return None
|
||||||
|
@ -60,6 +60,10 @@ the database until you explicitly call ``save()``.
|
|||||||
|
|
||||||
The ``save()`` method has no return value.
|
The ``save()`` method has no return value.
|
||||||
|
|
||||||
|
To create an object and save it all in one step see the `create`__ method.
|
||||||
|
|
||||||
|
__ `create(**kwargs)`_
|
||||||
|
|
||||||
Auto-incrementing primary keys
|
Auto-incrementing primary keys
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@ -705,6 +709,20 @@ The ``DoesNotExist`` exception inherits from
|
|||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
print "Either the entry or blog doesn't exist."
|
print "Either the entry or blog doesn't exist."
|
||||||
|
|
||||||
|
``create(**kwargs)``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A convenience method for creating an object and saving it all in one step. Thus::
|
||||||
|
|
||||||
|
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
|
||||||
|
|
||||||
|
and::
|
||||||
|
|
||||||
|
p = Person(first_name="Bruce", last_name="Springsteen")
|
||||||
|
p.save()
|
||||||
|
|
||||||
|
are equivalent.
|
||||||
|
|
||||||
``get_or_create(**kwargs)``
|
``get_or_create(**kwargs)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
85
docs/serialization.txt
Normal file
85
docs/serialization.txt
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
==========================
|
||||||
|
Serializing Django objects
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This API is currently under heavy development and may change --
|
||||||
|
perhaps drastically -- in the future.
|
||||||
|
|
||||||
|
You have been warned.
|
||||||
|
|
||||||
|
Django's serialization framework provides a mechanism for "translating" Django
|
||||||
|
objects into other formats. Usually these other formats will be text-based and
|
||||||
|
used for sending Django objects over a wire, but it's possible for a
|
||||||
|
serializer to handle any format (text-based or not).
|
||||||
|
|
||||||
|
Serializing data
|
||||||
|
----------------
|
||||||
|
|
||||||
|
At the highest level, serializing data is a very simple operation::
|
||||||
|
|
||||||
|
from django.core import serializers
|
||||||
|
data = serializers.serialize("xml", SomeModel.objects.all())
|
||||||
|
|
||||||
|
The arguments to the ``serialize`` function are the format to serialize the
|
||||||
|
data to (see `Serialization formats`_) and a QuerySet_ to serialize.
|
||||||
|
(Actually, the second argument can be any iterator that yields Django objects,
|
||||||
|
but it'll almost always be a QuerySet).
|
||||||
|
|
||||||
|
.. _QuerySet: ../db_api/#retrieving-objects
|
||||||
|
|
||||||
|
You can also use a serializer object directly::
|
||||||
|
|
||||||
|
xml_serializer = serializers.get_serializer("xml")
|
||||||
|
xml_serializer.serialize(queryset)
|
||||||
|
data = xml_serializer.getvalue()
|
||||||
|
|
||||||
|
This is useful if you want to serialize data directly to a file-like object
|
||||||
|
(which includes a HTTPResponse_)::
|
||||||
|
|
||||||
|
out = open("file.xml", "w")
|
||||||
|
xml_serializer.serialize(SomeModel.objects.all(), stream=out)
|
||||||
|
|
||||||
|
.. _HTTPResponse: ../request_response/#httpresponse-objects
|
||||||
|
|
||||||
|
Deserializing data
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Deserializing data is also a fairly simple operation::
|
||||||
|
|
||||||
|
for obj in serializers.deserialize("xml", data):
|
||||||
|
do_something_with(obj)
|
||||||
|
|
||||||
|
As you can see, the ``deserialize`` function takes the same format argument as
|
||||||
|
``serialize``, a string or stream of data, and returns an iterator.
|
||||||
|
|
||||||
|
However, here it gets slightly complicated. The objects returned by the
|
||||||
|
``deserialize`` iterator *aren't* simple Django objects. Instead, they are
|
||||||
|
special ``DeserializedObject`` instances that wrap a created -- but unsaved --
|
||||||
|
object and any associated relationship data.
|
||||||
|
|
||||||
|
Calling ``DeserializedObject.save()`` saves the object to the database.
|
||||||
|
|
||||||
|
This ensures that deserializing is a non-destructive operation even if the
|
||||||
|
data in your serialized representation doesn't match what's currently in the
|
||||||
|
database. Usually, working with these ``DeserializedObject`` instances looks
|
||||||
|
something like::
|
||||||
|
|
||||||
|
for deserialized_object in serializers.deserialize("xml", data):
|
||||||
|
if object_should_be_saved(deserialized_object):
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
In other words, the usual use is to examine the deserialized objects to make
|
||||||
|
sure that they are "appropriate" for saving before doing so. Of course, if you trust your data source you could just save the object and move on.
|
||||||
|
|
||||||
|
The Django object itself can be inspected as ``deserialized_object.object``.
|
||||||
|
|
||||||
|
Serialization formats
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Django "ships" with a few included serializers, and there's a simple API for creating and registering your own...
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
... which will be documented once the API is stable :)
|
@ -347,4 +347,9 @@ API_TESTS += """
|
|||||||
>>> a101 = Article.objects.get(pk=101)
|
>>> a101 = Article.objects.get(pk=101)
|
||||||
>>> a101.headline
|
>>> a101.headline
|
||||||
'Article 101'
|
'Article 101'
|
||||||
|
|
||||||
|
# You can create saved objects in a single step
|
||||||
|
>>> a10 = Article.objects.create(headline="Article 10", pub_date=datetime(2005, 7, 31, 12, 30, 45))
|
||||||
|
>>> Article.objects.get(headline="Article 10")
|
||||||
|
<Article: Article 10>
|
||||||
"""
|
"""
|
||||||
|
@ -58,6 +58,10 @@ Article 4
|
|||||||
>>> Article.objects.filter(headline__startswith='Blah blah').count()
|
>>> Article.objects.filter(headline__startswith='Blah blah').count()
|
||||||
0L
|
0L
|
||||||
|
|
||||||
|
# Date and date/time lookups can also be done with strings.
|
||||||
|
>>> Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count()
|
||||||
|
3L
|
||||||
|
|
||||||
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
|
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
|
||||||
# to objects.
|
# to objects.
|
||||||
>>> Article.objects.in_bulk([1, 2])
|
>>> Article.objects.in_bulk([1, 2])
|
||||||
|
0
tests/modeltests/serializers/__init__.py
Normal file
0
tests/modeltests/serializers/__init__.py
Normal file
94
tests/modeltests/serializers/models.py
Normal file
94
tests/modeltests/serializers/models.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
"""
|
||||||
|
XXX. Serialization
|
||||||
|
|
||||||
|
``django.core.serializers`` provides interfaces to converting Django querysets
|
||||||
|
to and from "flat" data (i.e. strings).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Category(models.Model):
|
||||||
|
name = models.CharField(maxlength=20)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('name',)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
name = models.CharField(maxlength=20)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('name',)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
author = models.ForeignKey(Author)
|
||||||
|
headline = models.CharField(maxlength=50)
|
||||||
|
pub_date = models.DateTimeField()
|
||||||
|
categories = models.ManyToManyField(Category)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('pub_date',)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.headline
|
||||||
|
|
||||||
|
API_TESTS = """
|
||||||
|
# Create some data:
|
||||||
|
>>> from datetime import datetime
|
||||||
|
>>> sports = Category(name="Sports")
|
||||||
|
>>> music = Category(name="Music")
|
||||||
|
>>> op_ed = Category(name="Op-Ed")
|
||||||
|
>>> sports.save(); music.save(); op_ed.save()
|
||||||
|
|
||||||
|
>>> joe = Author(name="Joe")
|
||||||
|
>>> jane = Author(name="Jane")
|
||||||
|
>>> joe.save(); jane.save()
|
||||||
|
|
||||||
|
>>> a1 = Article(
|
||||||
|
... author = jane,
|
||||||
|
... headline = "Poker has no place on ESPN",
|
||||||
|
... pub_date = datetime(2006, 6, 16, 11, 00))
|
||||||
|
>>> a2 = Article(
|
||||||
|
... author = joe,
|
||||||
|
... headline = "Time to reform copyright",
|
||||||
|
... pub_date = datetime(2006, 6, 16, 13, 00))
|
||||||
|
>>> a1.save(); a2.save()
|
||||||
|
>>> a1.categories = [sports, op_ed]
|
||||||
|
>>> a2.categories = [music, op_ed]
|
||||||
|
|
||||||
|
# Serialize a queryset to XML
|
||||||
|
>>> from django.core import serializers
|
||||||
|
>>> xml = serializers.serialize("xml", Article.objects.all())
|
||||||
|
|
||||||
|
# The output is valid XML
|
||||||
|
>>> from xml.dom import minidom
|
||||||
|
>>> dom = minidom.parseString(xml)
|
||||||
|
|
||||||
|
# Deserializing has a similar interface, except that special DeserializedObject
|
||||||
|
# instances are returned. This is because data might have changed in the
|
||||||
|
# database since the data was serialized (we'll simulate that below).
|
||||||
|
>>> for obj in serializers.deserialize("xml", xml):
|
||||||
|
... print obj
|
||||||
|
<DeserializedObject: Poker has no place on ESPN>
|
||||||
|
<DeserializedObject: Time to reform copyright>
|
||||||
|
|
||||||
|
# Deserializing data with different field values doesn't change anything in the
|
||||||
|
# database until we call save():
|
||||||
|
>>> xml = xml.replace("Poker has no place on ESPN", "Poker has no place on television")
|
||||||
|
>>> objs = list(serializers.deserialize("xml", xml))
|
||||||
|
|
||||||
|
# Even those I deserialized, the database hasn't been touched
|
||||||
|
>>> Article.objects.all()
|
||||||
|
[<Article: Poker has no place on ESPN>, <Article: Time to reform copyright>]
|
||||||
|
|
||||||
|
# But when I save, the data changes as you might except.
|
||||||
|
>>> objs[0].save()
|
||||||
|
>>> Article.objects.all()
|
||||||
|
[<Article: Poker has no place on television>, <Article: Time to reform copyright>]
|
||||||
|
|
||||||
|
"""
|
Loading…
x
Reference in New Issue
Block a user