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
|
||||
# Django-powered features.
|
||||
BANNED_IPS = ()
|
||||
|
||||
##################
|
||||
# AUTHENTICATION #
|
||||
##################
|
||||
|
||||
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django import http, template
|
||||
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.utils.translation import gettext_lazy
|
||||
import base64, datetime, md5
|
||||
@ -69,10 +70,10 @@ def staff_member_required(view_func):
|
||||
return _display_login_form(request, message)
|
||||
|
||||
# Check the password.
|
||||
username = request.POST.get('username', '')
|
||||
try:
|
||||
user = User.objects.get(username=username, is_staff=True)
|
||||
except User.DoesNotExist:
|
||||
username = request.POST.get('username', None)
|
||||
password = request.POST.get('password', None)
|
||||
user = authenticate(username=username, password=password)
|
||||
if user is None:
|
||||
message = ERROR_MESSAGE
|
||||
if '@' in username:
|
||||
# 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.
|
||||
else:
|
||||
if user.check_password(request.POST.get('password', '')):
|
||||
request.session[SESSION_KEY] = user.id
|
||||
if user.is_staff:
|
||||
login(request, user)
|
||||
# TODO: set last_login with an event.
|
||||
user.last_login = datetime.datetime.now()
|
||||
user.save()
|
||||
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/'
|
||||
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 import authenticate
|
||||
from django.contrib.sites.models import Site
|
||||
from django.template import Context, loader
|
||||
from django.core import validators
|
||||
@ -20,8 +21,7 @@ class AuthenticationForm(forms.Manipulator):
|
||||
self.fields = [
|
||||
forms.TextField(field_name="username", length=15, maxlength=30, is_required=True,
|
||||
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
|
||||
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True,
|
||||
validator_list=[self.isValidPasswordForUser]),
|
||||
forms.PasswordField(field_name="password", length=15, maxlength=30, is_required=True),
|
||||
]
|
||||
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.")
|
||||
|
||||
def isValidUser(self, field_data, all_data):
|
||||
try:
|
||||
self.user_cache = User.objects.get(username=field_data)
|
||||
except User.DoesNotExist:
|
||||
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
|
||||
|
||||
def isValidPasswordForUser(self, field_data, all_data):
|
||||
username = field_data
|
||||
password = all_data.get('password', None)
|
||||
self.user_cache = authenticate(username=username, password=password)
|
||||
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.")
|
||||
elif not self.user_cache.is_active:
|
||||
raise validators.ValidationError, _("This account is inactive.")
|
||||
|
@ -4,12 +4,8 @@ class LazyUser(object):
|
||||
|
||||
def __get__(self, request, obj_type=None):
|
||||
if self._user is None:
|
||||
from django.contrib.auth.models import User, AnonymousUser, SESSION_KEY
|
||||
try:
|
||||
user_id = request.session[SESSION_KEY]
|
||||
self._user = User.objects.get(pk=user_id)
|
||||
except (KeyError, User.DoesNotExist):
|
||||
self._user = AnonymousUser()
|
||||
from django.contrib.auth import get_user
|
||||
self._user = get_user(request)
|
||||
return self._user
|
||||
|
||||
class AuthenticationMiddleware(object):
|
||||
|
@ -4,7 +4,19 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
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):
|
||||
pass
|
||||
@ -141,14 +153,7 @@ class User(models.Model):
|
||||
self.set_password(raw_password)
|
||||
self.save()
|
||||
return is_correct
|
||||
algo, salt, hsh = self.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."
|
||||
return check_password(raw_password, self.password)
|
||||
|
||||
def get_group_permissions(self):
|
||||
"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.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.contrib.auth.models import SESSION_KEY
|
||||
from django.contrib.sites.models import Site
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
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.
|
||||
if not redirect_to or '://' in redirect_to or ' ' in redirect_to:
|
||||
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()
|
||||
return HttpResponseRedirect(redirect_to)
|
||||
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'):
|
||||
"Logs out the user and displays 'You are logged out' message."
|
||||
from django.contrib.auth import logout
|
||||
try:
|
||||
del request.session[SESSION_KEY]
|
||||
logout(request)
|
||||
except KeyError:
|
||||
return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request))
|
||||
else:
|
||||
|
@ -5,7 +5,6 @@ from django.http import Http404
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.shortcuts import render_to_response
|
||||
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.contenttypes.models import ContentType
|
||||
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
|
||||
# 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']):
|
||||
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'):
|
||||
class CommentFormWrapper(forms.FormWrapper):
|
||||
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."
|
||||
return value
|
||||
else:
|
||||
return textile.textile(value)
|
||||
return textile.textile(value, encoding=settings.DEFAULT_CHARSET, output=settings.DEFAULT_CHARSET)
|
||||
|
||||
def markdown(value):
|
||||
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()
|
||||
# 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" % \
|
||||
(backend.quote_name(ordered_obj.db_table), backend.quote_name('_order'),
|
||||
backend.quote_name(ordered_obj.order_with_respect_to.column),
|
||||
backend.quote_name(ordered_obj.pk.column))
|
||||
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
|
||||
(backend.quote_name(ordered_obj._meta.db_table), backend.quote_name('_order'),
|
||||
backend.quote_name(ordered_obj._meta.order_with_respect_to.column),
|
||||
backend.quote_name(ordered_obj._meta.pk.column))
|
||||
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)])
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
|
@ -411,7 +411,7 @@ class DateField(Field):
|
||||
def get_db_prep_lookup(self, lookup_type, value):
|
||||
if lookup_type == 'range':
|
||||
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')
|
||||
else:
|
||||
value = str(value)
|
||||
|
@ -32,18 +32,25 @@ def get_apps():
|
||||
_app_errors[app_name] = e
|
||||
return _app_list
|
||||
|
||||
def get_app(app_label):
|
||||
"Returns the module containing the models for the given app_label."
|
||||
def get_app(app_label, emptyOK = False):
|
||||
"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.
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
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
|
||||
|
||||
def load_app(app_name):
|
||||
"Loads the app with the provided fully qualified name, and returns the model module."
|
||||
global _app_list
|
||||
mod = __import__(app_name, '', '', ['models'])
|
||||
if not hasattr(mod, 'models'):
|
||||
return None
|
||||
if mod.models not in _app_list:
|
||||
_app_list.append(mod.models)
|
||||
return mod.models
|
||||
|
@ -69,8 +69,11 @@ class Manager(object):
|
||||
def get(self, *args, **kwargs):
|
||||
return self.get_query_set().get(*args, **kwargs)
|
||||
|
||||
def get_or_create(self, *args, **kwargs):
|
||||
return self.get_query_set().get_or_create(*args, **kwargs)
|
||||
def get_or_create(self, **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):
|
||||
return self.get_query_set().filter(*args, **kwargs)
|
||||
|
@ -205,6 +205,15 @@ class QuerySet(object):
|
||||
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]
|
||||
|
||||
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):
|
||||
"""
|
||||
Looks up an object with the given kwargs, creating one if necessary.
|
||||
|
@ -267,17 +267,25 @@ previous section). You can tell them apart with ``is_anonymous()``, like so::
|
||||
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
|
||||
request.session[SESSION_KEY] = some_user.id
|
||||
from django.contrib.auth import authenticate, login
|
||||
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
|
||||
``SessionMiddleware`` enabled. See the `session documentation`_ for more
|
||||
information.
|
||||
``authenticate`` checks the username and password. If they are valid it
|
||||
returns a user object, otherwise it returns ``None``. ``login`` makes it so
|
||||
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
|
||||
----------------------------------
|
||||
@ -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`_.
|
||||
|
||||
.. _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.
|
||||
|
||||
To create an object and save it all in one step see the `create`__ method.
|
||||
|
||||
__ `create(**kwargs)`_
|
||||
|
||||
Auto-incrementing primary keys
|
||||
------------------------------
|
||||
|
||||
@ -705,6 +709,20 @@ The ``DoesNotExist`` exception inherits from
|
||||
except ObjectDoesNotExist:
|
||||
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)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
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.headline
|
||||
'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()
|
||||
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
|
||||
# to objects.
|
||||
>>> 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