1
0
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:
Alex Gaynor 2010-08-09 21:16:59 +00:00
parent 9b263c61f8
commit c7bd48cb9f
6 changed files with 137 additions and 4 deletions

View File

@ -172,6 +172,10 @@ class SQLUpdateCompiler(SQLCompiler):
vals = {}
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"):
assert value.connector in (value.ADD, value.SUB)
assert not value.negated

View File

@ -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.related import (ForeignKey, OneToOneField,
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
# Admin stages.

View File

@ -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.subclassing import SubfieldBase
class ListField(Field):
__metaclass__ = SubfieldBase
def __init__(self, field_type):
self.field_type = field_type
super(ListField, self).__init__()
super(ListField, self).__init__(default=[])
def get_prep_lookup(self, 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(
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)

View File

@ -47,6 +47,7 @@ def is_protected_type(obj):
return isinstance(obj, (
types.NoneType,
int, long,
list,
datetime.datetime, datetime.date, datetime.time,
float, Decimal)
)

View File

@ -31,3 +31,23 @@ class Post(models.Model):
magic_numbers = models.ListField(
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)
)

View File

@ -1,8 +1,9 @@
from django.core.exceptions import ValidationError
from django.db import connection, UnsupportedDatabaseOperation
from django.db.models import Count, Sum, F, Q
from django.test import TestCase
from models import Artist, Group, Post
from models import Artist, Group, Post, WikiPage, Revision, AuthenticatedRevision
class MongoTestCase(TestCase):
@ -398,6 +399,9 @@ class MongoTestCase(TestCase):
)
def test_list_field(self):
p = Post()
self.assertEqual(p.tags, [])
p = Post.objects.create(
title="Django ORM grows MongoDB support",
tags=["python", "django", "mongodb", "web"]
@ -428,7 +432,7 @@ class MongoTestCase(TestCase):
lambda p: p.title
)
self.assertRaises(ValueError,
self.assertRaises(ValidationError,
lambda: Post.objects.create(magic_numbers=["a"])
)
@ -448,3 +452,52 @@ class MongoTestCase(TestCase):
],
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)