feat: add support for association proxy fields in model forms
This commit is contained in:
parent
e1122caa9c
commit
48f96220b6
4 changed files with 338 additions and 4 deletions
|
|
@ -4,11 +4,16 @@ import os
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.ext.associationproxy import AssociationProxy
|
||||
|
||||
import colander
|
||||
import deform
|
||||
import colander
|
||||
from colanderalchemy import SQLAlchemySchemaNode
|
||||
from pyramid.renderers import get_renderer
|
||||
|
||||
from wuttjamaican.testing import DataTestCase
|
||||
|
||||
from wuttaweb.forms import base as mod, widgets
|
||||
from wuttaweb.grids import Grid
|
||||
from wuttaweb.testing import WebTestCase
|
||||
|
|
@ -734,3 +739,174 @@ class TestForm(WebTestCase):
|
|||
self.assertNotIn("foo", dform)
|
||||
self.assertIn("bar", schema)
|
||||
self.assertIn("bar", dform)
|
||||
|
||||
|
||||
class TestWuttaSchemaNode(WebTestCase):
|
||||
|
||||
def test_add_nodes(self):
|
||||
model = self.app.model
|
||||
|
||||
# ColanderAlchemy does not add nodes for association proxy fields
|
||||
schema = SQLAlchemySchemaNode(
|
||||
model.User, includes=["username", "first_name", "last_name"]
|
||||
)
|
||||
self.assertIn("username", schema)
|
||||
self.assertNotIn("first_name", schema)
|
||||
self.assertNotIn("last_name", schema)
|
||||
|
||||
# but our custom class handles them okay
|
||||
schema = mod.WuttaSchemaNode(
|
||||
model.User, includes=["username", "first_name", "last_name"]
|
||||
)
|
||||
self.assertIn("username", schema)
|
||||
self.assertIn("first_name", schema)
|
||||
self.assertIn("last_name", schema)
|
||||
|
||||
def test_dictify(self):
|
||||
model = self.app.model
|
||||
|
||||
person = model.Person(first_name="Fred", last_name="Flintstone")
|
||||
user = model.User(username="freddie", person=person)
|
||||
user.foo_field = "bar"
|
||||
|
||||
foo_field = colander.SchemaNode(colander.String())
|
||||
|
||||
# ColanderAlchemy does not include association proxy fields
|
||||
schema = SQLAlchemySchemaNode(
|
||||
model.User, includes=["username", "first_name", "last_name", foo_field]
|
||||
)
|
||||
dct = schema.dictify(user)
|
||||
self.assertIn("username", dct)
|
||||
self.assertEqual(dct["username"], "freddie")
|
||||
self.assertNotIn("first_name", dct)
|
||||
self.assertNotIn("last_name", dct)
|
||||
self.assertNotIn("foo_field", dct)
|
||||
|
||||
# but our custom class handles them okay
|
||||
schema = mod.WuttaSchemaNode(
|
||||
model.User, includes=["username", "first_name", "last_name", foo_field]
|
||||
)
|
||||
dct = schema.dictify(user)
|
||||
self.assertIn("username", dct)
|
||||
self.assertEqual(dct["username"], "freddie")
|
||||
self.assertIn("first_name", dct)
|
||||
self.assertEqual(dct["first_name"], "Fred")
|
||||
self.assertIn("last_name", dct)
|
||||
self.assertEqual(dct["last_name"], "Flintstone")
|
||||
# nb. foo_field is not assn proxy, so not auto-dictified
|
||||
self.assertNotIn("foo_field", dct)
|
||||
|
||||
# also note special handling for null values on string proxy fields
|
||||
user.last_name = None
|
||||
dct = schema.dictify(user)
|
||||
self.assertIn("first_name", dct)
|
||||
self.assertIs(dct["first_name"], "Fred")
|
||||
self.assertIn("last_name", dct)
|
||||
self.assertIsNotNone(dct["last_name"])
|
||||
self.assertIs(dct["last_name"], colander.null)
|
||||
|
||||
def test_objectify(self):
|
||||
model = self.app.model
|
||||
|
||||
# ColanderAlchemy does not set association proxy fields
|
||||
schema = SQLAlchemySchemaNode(
|
||||
model.User,
|
||||
includes=["username", "first_name", "last_name"],
|
||||
)
|
||||
user = schema.objectify(
|
||||
{
|
||||
"username": "freddie",
|
||||
"first_name": "Fred",
|
||||
"last_name": "Flintstone",
|
||||
}
|
||||
)
|
||||
self.assertIsInstance(user, model.User)
|
||||
self.assertEqual(user.username, "freddie")
|
||||
self.assertIsNone(user.person)
|
||||
self.assertIsNone(user.first_name)
|
||||
self.assertIsNone(user.last_name)
|
||||
|
||||
# but our custom class handles them okay
|
||||
schema = mod.WuttaSchemaNode(
|
||||
model.User,
|
||||
includes=["username", "first_name", "last_name"],
|
||||
)
|
||||
user = schema.objectify(
|
||||
{
|
||||
"username": "freddie",
|
||||
"first_name": "Fred",
|
||||
"last_name": "Flintstone",
|
||||
}
|
||||
)
|
||||
self.assertIsInstance(user, model.User)
|
||||
self.assertEqual(user.username, "freddie")
|
||||
self.assertIsInstance(user.person, model.Person)
|
||||
self.assertEqual(user.person.first_name, "Fred")
|
||||
self.assertEqual(user.person.last_name, "Flintstone")
|
||||
self.assertEqual(user.first_name, "Fred")
|
||||
self.assertEqual(user.last_name, "Flintstone")
|
||||
|
||||
# also note special handling for null values on string proxy fields
|
||||
user = schema.objectify(
|
||||
{
|
||||
"username": "freddie",
|
||||
"first_name": "Fred",
|
||||
"last_name": colander.null,
|
||||
}
|
||||
)
|
||||
self.assertIsInstance(user, model.User)
|
||||
self.assertEqual(user.username, "freddie")
|
||||
self.assertIsInstance(user.person, model.Person)
|
||||
self.assertEqual(user.person.first_name, "Fred")
|
||||
self.assertIsNone(user.person.last_name)
|
||||
self.assertEqual(user.first_name, "Fred")
|
||||
self.assertIsNone(user.last_name)
|
||||
|
||||
|
||||
class TestGetAssociationProxy(DataTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
model = self.app.model
|
||||
mapper = sa.inspect(model.User)
|
||||
|
||||
# typical
|
||||
proxy = mod.get_association_proxy(mapper, "first_name")
|
||||
self.assertIsInstance(proxy, AssociationProxy)
|
||||
self.assertEqual(proxy.target_collection, "person")
|
||||
self.assertEqual(proxy.value_attr, "first_name")
|
||||
|
||||
# no such proxy
|
||||
proxy = mod.get_association_proxy(mapper, "blarg")
|
||||
self.assertIsNone(proxy)
|
||||
|
||||
|
||||
class TestGetAssociationProxyTarget(DataTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
model = self.app.model
|
||||
mapper = sa.inspect(model.User)
|
||||
|
||||
# typical
|
||||
target = mod.get_association_proxy_target(mapper, "first_name")
|
||||
self.assertIsInstance(target, orm.RelationshipProperty)
|
||||
self.assertIs(target, mapper.get_property("person"))
|
||||
|
||||
# no such proxy
|
||||
target = mod.get_association_proxy_target(mapper, "blarg")
|
||||
self.assertIsNone(target)
|
||||
|
||||
|
||||
class TestGetAssociationProxyColumn(DataTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
model = self.app.model
|
||||
mapper = sa.inspect(model.User)
|
||||
|
||||
# typical
|
||||
column = mod.get_association_proxy_column(mapper, "first_name")
|
||||
self.assertIsInstance(column, orm.ColumnProperty)
|
||||
self.assertIs(column, sa.inspect(model.Person).get_property("first_name"))
|
||||
|
||||
# no such proxy
|
||||
column = mod.get_association_proxy_column(mapper, "blarg")
|
||||
self.assertIsNone(column)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue