1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +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:
Jason Pellerin 2006-06-28 17:07:26 +00:00
parent 54b6e96957
commit da5b0586d3
25 changed files with 918 additions and 57 deletions

View File

@ -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',)

View File

@ -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'):

View File

@ -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

View 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

View File

@ -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.")

View File

@ -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):

View File

@ -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."

View File

@ -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:

View File

@ -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):

View File

@ -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:

View 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])

View 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

View 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)

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -204,6 +204,15 @@ class QuerySet(object):
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)
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):
"""

View File

@ -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

View File

@ -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
View 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 :)

View File

@ -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>
"""

View File

@ -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])

View File

View 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>]
"""