mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
[soc2010/query-refactor] Improved the ListField implementation, and added an EmbeddedModelField.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13564 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
9b263c61f8
commit
c7bd48cb9f
@ -172,6 +172,10 @@ class SQLUpdateCompiler(SQLCompiler):
|
|||||||
|
|
||||||
vals = {}
|
vals = {}
|
||||||
for field, o, value in self.query.values:
|
for field, o, value in self.query.values:
|
||||||
|
if hasattr(value, 'prepare_database_save'):
|
||||||
|
value = value.prepare_database_save(field)
|
||||||
|
else:
|
||||||
|
value = field.get_db_prep_save(value, connection=self.connection)
|
||||||
if hasattr(value, "evaluate"):
|
if hasattr(value, "evaluate"):
|
||||||
assert value.connector in (value.ADD, value.SUB)
|
assert value.connector in (value.ADD, value.SUB)
|
||||||
assert not value.negated
|
assert not value.negated
|
||||||
|
@ -13,7 +13,7 @@ from django.db.models.fields.subclassing import SubfieldBase
|
|||||||
from django.db.models.fields.files import FileField, ImageField
|
from django.db.models.fields.files import FileField, ImageField
|
||||||
from django.db.models.fields.related import (ForeignKey, OneToOneField,
|
from django.db.models.fields.related import (ForeignKey, OneToOneField,
|
||||||
ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel)
|
ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel)
|
||||||
from django.db.models.fields.structures import ListField
|
from django.db.models.fields.structures import ListField, EmbeddedModel
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
|
|
||||||
# Admin stages.
|
# Admin stages.
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db.models.loading import cache
|
||||||
from django.db.models.fields import Field
|
from django.db.models.fields import Field
|
||||||
|
from django.db.models.fields.subclassing import SubfieldBase
|
||||||
|
|
||||||
|
|
||||||
class ListField(Field):
|
class ListField(Field):
|
||||||
|
__metaclass__ = SubfieldBase
|
||||||
|
|
||||||
def __init__(self, field_type):
|
def __init__(self, field_type):
|
||||||
self.field_type = field_type
|
self.field_type = field_type
|
||||||
super(ListField, self).__init__()
|
super(ListField, self).__init__(default=[])
|
||||||
|
|
||||||
def get_prep_lookup(self, lookup_type, value):
|
def get_prep_lookup(self, lookup_type, value):
|
||||||
return self.field_type.get_prep_lookup(lookup_type, value)
|
return self.field_type.get_prep_lookup(lookup_type, value)
|
||||||
@ -19,3 +24,53 @@ class ListField(Field):
|
|||||||
return self.field_type.get_db_prep_lookup(
|
return self.field_type.get_db_prep_lookup(
|
||||||
lookup_type, value, connection=connection, prepared=prepared
|
lookup_type, value, connection=connection, prepared=prepared
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
try:
|
||||||
|
value = iter(value)
|
||||||
|
except TypeError:
|
||||||
|
raise ValidationError("Value should be iterable")
|
||||||
|
return [
|
||||||
|
self.field_type.to_python(v)
|
||||||
|
for v in value
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EmbeddedModel(Field):
|
||||||
|
__metaclass__ = SubfieldBase
|
||||||
|
|
||||||
|
def __init__(self, to):
|
||||||
|
self.to = to
|
||||||
|
super(EmbeddedModel, self).__init__()
|
||||||
|
|
||||||
|
def get_db_prep_save(self, value, connection):
|
||||||
|
data = {}
|
||||||
|
if not isinstance(value, self.to):
|
||||||
|
raise ValidationError("Value must be an instance of %s, got %s "
|
||||||
|
"instead" % (self.to, value))
|
||||||
|
if type(value) is not self.to:
|
||||||
|
data["_cls"] = (value._meta.app_label, value._meta.object_name)
|
||||||
|
for field in value._meta.fields:
|
||||||
|
# If the field is a OneToOneField that makes the inheritance link,
|
||||||
|
# ignore it.
|
||||||
|
if field.rel and field.rel.parent_link:
|
||||||
|
continue
|
||||||
|
data[field.column] = field.get_db_prep_save(
|
||||||
|
getattr(value, field.name), connection=connection
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if isinstance(value, self.to):
|
||||||
|
return value
|
||||||
|
try:
|
||||||
|
value = dict(value)
|
||||||
|
except TypeError:
|
||||||
|
raise ValidationError("Value should be a dict")
|
||||||
|
|
||||||
|
if "_cls" in value:
|
||||||
|
cls = cache.get_model(*value.pop("_cls"))
|
||||||
|
else:
|
||||||
|
cls = self.to
|
||||||
|
|
||||||
|
return cls(**value)
|
||||||
|
@ -47,6 +47,7 @@ def is_protected_type(obj):
|
|||||||
return isinstance(obj, (
|
return isinstance(obj, (
|
||||||
types.NoneType,
|
types.NoneType,
|
||||||
int, long,
|
int, long,
|
||||||
|
list,
|
||||||
datetime.datetime, datetime.date, datetime.time,
|
datetime.datetime, datetime.date, datetime.time,
|
||||||
float, Decimal)
|
float, Decimal)
|
||||||
)
|
)
|
||||||
|
@ -31,3 +31,23 @@ class Post(models.Model):
|
|||||||
magic_numbers = models.ListField(
|
magic_numbers = models.ListField(
|
||||||
models.IntegerField()
|
models.IntegerField()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Revision(models.Model):
|
||||||
|
number = models.IntegerField()
|
||||||
|
content = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticatedRevision(Revision):
|
||||||
|
# This is a really stupid way to add optional authentication, but it serves
|
||||||
|
# its purpose.
|
||||||
|
author = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
|
class WikiPage(models.Model):
|
||||||
|
id = models.NativeAutoField(primary_key=True)
|
||||||
|
title = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
revisions = models.ListField(
|
||||||
|
models.EmbeddedModel(Revision)
|
||||||
|
)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import connection, UnsupportedDatabaseOperation
|
from django.db import connection, UnsupportedDatabaseOperation
|
||||||
from django.db.models import Count, Sum, F, Q
|
from django.db.models import Count, Sum, F, Q
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from models import Artist, Group, Post
|
from models import Artist, Group, Post, WikiPage, Revision, AuthenticatedRevision
|
||||||
|
|
||||||
|
|
||||||
class MongoTestCase(TestCase):
|
class MongoTestCase(TestCase):
|
||||||
@ -398,6 +399,9 @@ class MongoTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_list_field(self):
|
def test_list_field(self):
|
||||||
|
p = Post()
|
||||||
|
self.assertEqual(p.tags, [])
|
||||||
|
|
||||||
p = Post.objects.create(
|
p = Post.objects.create(
|
||||||
title="Django ORM grows MongoDB support",
|
title="Django ORM grows MongoDB support",
|
||||||
tags=["python", "django", "mongodb", "web"]
|
tags=["python", "django", "mongodb", "web"]
|
||||||
@ -428,7 +432,7 @@ class MongoTestCase(TestCase):
|
|||||||
lambda p: p.title
|
lambda p: p.title
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValidationError,
|
||||||
lambda: Post.objects.create(magic_numbers=["a"])
|
lambda: Post.objects.create(magic_numbers=["a"])
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -448,3 +452,52 @@ class MongoTestCase(TestCase):
|
|||||||
],
|
],
|
||||||
lambda p: p.title,
|
lambda p: p.title,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_embedded_model(self):
|
||||||
|
page = WikiPage(title="Django")
|
||||||
|
page.revisions.append(
|
||||||
|
Revision(number=1, content="Django is a Python")
|
||||||
|
)
|
||||||
|
page.revisions.append(
|
||||||
|
Revision(number=2, content="Django is a Python web framework.")
|
||||||
|
)
|
||||||
|
|
||||||
|
page.save()
|
||||||
|
|
||||||
|
page = WikiPage.objects.get(pk=page.pk)
|
||||||
|
self.assertEqual(len(page.revisions), 2)
|
||||||
|
self.assertEqual(
|
||||||
|
[(r.number, r.content) for r in page.revisions],
|
||||||
|
[(1, "Django is a Python"), (2, "Django is a Python web framework.")]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(Revision.objects.count(), 0)
|
||||||
|
|
||||||
|
self.assertRaises(ValidationError,
|
||||||
|
lambda: WikiPage.objects.create(title="Python", revisions=14)
|
||||||
|
)
|
||||||
|
self.assertRaises(ValidationError,
|
||||||
|
lambda: WikiPage.objects.create(title="Python", revisions=[14])
|
||||||
|
)
|
||||||
|
|
||||||
|
page = WikiPage.objects.create(title="Python", revisions=[
|
||||||
|
Revision(number=1, content="Python was created by Guido van Rossum.")
|
||||||
|
])
|
||||||
|
page = WikiPage.objects.get(pk=page.pk)
|
||||||
|
self.assertEqual(len(page.revisions), 1)
|
||||||
|
|
||||||
|
page.revisions.append(
|
||||||
|
AuthenticatedRevision(number=2, content="Python is a trap.", author="Rasmus Lerdorf"),
|
||||||
|
)
|
||||||
|
|
||||||
|
page.save()
|
||||||
|
self.assertEqual(len(page.revisions), 2)
|
||||||
|
self.assertEqual(page.revisions[-1].author, "Rasmus Lerdorf")
|
||||||
|
|
||||||
|
page = WikiPage.objects.get(pk=page.pk)
|
||||||
|
self.assertEqual(len(page.revisions), 2)
|
||||||
|
self.assertTrue(isinstance(page.revisions[-1], AuthenticatedRevision))
|
||||||
|
self.assertEqual(page.revisions[-1].author, "Rasmus Lerdorf")
|
||||||
|
|
||||||
|
page.revisions.append(14)
|
||||||
|
self.assertRaises(ValidationError, page.save)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user