Skip to content

Commit c43b619

Browse files
committed
fix multiple json profiles
1 parent 429c4d2 commit c43b619

File tree

4 files changed

+74
-16
lines changed

4 files changed

+74
-16
lines changed

src/gino/crud.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,10 @@ def update(self, **values):
222222
k = key
223223
if not isinstance(value, ClauseElement):
224224
setattr(self._instance, key, value)
225-
value = getattr(self._instance, value_from)[key]
225+
if isinstance(prop, json_support.JSONProperty):
226+
value = prop.get_profile(self._instance)[key]
227+
else:
228+
value = getattr(self._instance, value_from)[key]
226229
method(k, value)
227230
return self
228231

@@ -448,7 +451,10 @@ async def _create(self, bind=None, timeout=DEFAULT):
448451
cls = type(self)
449452
# noinspection PyUnresolvedReferences,PyProtectedMember
450453
cls._check_abstract()
451-
profile_keys = set(self.__profile__.keys() if self.__profile__ else [])
454+
if self.__profile__:
455+
profile_keys = set([k for v in self.__profile__.values() for k in v.keys()])
456+
else:
457+
profile_keys = []
452458
for key in profile_keys:
453459
cls.__dict__.get(key).save(self)
454460
# initialize default values

src/gino/json_support.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ def __delete__(self, instance):
5252

5353
def get_profile(self, instance):
5454
if instance.__profile__ is None:
55-
props = type(instance).__dict__
5655
instance.__profile__ = {}
56+
if instance.__profile__.get(self.prop_name, None) is None:
57+
props = type(instance).__dict__
58+
current_profile = {}
59+
instance.__profile__[self.prop_name] = current_profile
5760
for key, value in (getattr(instance, self.prop_name, None) or {}).items():
5861
if key not in props:
5962
raise UnknownJSONPropertyError(
@@ -70,17 +73,16 @@ def get_profile(self, instance):
7073
key, self.prop_name, instance, key
7174
)
7275
)
73-
instance.__profile__[key] = prop.decode(value)
74-
75-
return instance.__profile__
76+
current_profile[key] = prop.decode(value)
77+
return instance.__profile__.get(self.prop_name)
7678

7779
def save(self, instance, value=NONE):
7880
profile = getattr(instance, self.prop_name, None)
7981
if profile is None:
8082
profile = {}
8183
setattr(instance, self.prop_name, profile)
8284
if value is NONE:
83-
value = instance.__profile__[self.name]
85+
value = self.get_profile(instance)[self.name]
8486
if not isinstance(value, sa.sql.ClauseElement):
8587
value = self.encode(value)
8688
rv = profile[self.name] = value
@@ -92,9 +94,9 @@ def reload(self, instance):
9294
profile = getattr(instance, self.prop_name, None) or {}
9395
value = profile.get(self.name, NONE)
9496
if value is NONE:
95-
instance.__profile__.pop(self.name, None)
97+
self.get_profile(instance).pop(self.name, None)
9698
else:
97-
instance.__profile__[self.name] = self.decode(value)
99+
self.get_profile(instance)[self.name] = self.decode(value)
98100

99101
def make_expression(self, base_exp):
100102
return base_exp

tests/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,16 @@ class User(db.Model):
3939
id = db.Column(db.BigInteger(), primary_key=True)
4040
nickname = db.Column("name", db.Unicode(), default=_random_name)
4141
profile = db.Column("props", JSONB(), nullable=False, server_default="{}")
42+
parameter = db.Column("params", JSONB(), nullable=False, server_default="{}")
4243
type = db.Column(db.Enum(UserType), nullable=False, default=UserType.USER,)
4344
realname = db.StringProperty()
4445
age = db.IntegerProperty(default=18)
4546
balance = db.IntegerProperty(default=0)
4647
birthday = db.DateTimeProperty(default=lambda i: datetime.utcfromtimestamp(0))
4748
team_id = db.Column(db.ForeignKey("gino_teams.id"))
49+
weight = db.IntegerProperty(prop_name='parameter')
50+
height = db.IntegerProperty(default=170, prop_name='parameter')
51+
bio = db.StringProperty(prop_name='parameter')
4852

4953
@balance.after_get
5054
def balance(self, val):

tests/test_json.py

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ async def test_in_memory():
1414
u.age += 10
1515
assert u.age == 28
1616
assert u.balance == 0
17+
assert u.height == 170
1718
assert isinstance(u.balance, float)
1819

1920

@@ -23,12 +24,13 @@ async def test_crud(bind):
2324

2425
now = datetime.utcnow()
2526
now_str = now.strftime(DATETIME_FORMAT)
26-
u = await User.create(nickname="fantix", birthday=now)
27+
u = await User.create(nickname="fantix", birthday=now, bio="I code in Python and more.")
2728
u.age += 1
2829
assert await u.query.gino.model(None).first() == (
2930
1,
3031
"fantix",
3132
{"age": 18, "birthday": now_str},
33+
{"bio": "I code in Python and more.", "height": 170},
3234
UserType.USER,
3335
None,
3436
)
@@ -38,23 +40,27 @@ async def test_crud(bind):
3840
assert u.birthday == now
3941
assert u.age == 18
4042
assert u.balance == 0
43+
assert u.height == 170
44+
assert u.bio == "I code in Python and more."
4145
assert isinstance(u.balance, float)
4246
assert await db.select([User.birthday]).where(User.id == u.id).gino.scalar() == now
4347

4448
# In-memory update, not applying
4549
u.update(birthday=now - timedelta(days=3650))
4650

4751
# Update two JSON fields, one using expression
48-
await u.update(age=User.age - 2, balance=100.85).apply()
52+
await u.update(age=User.age - 2, balance=100.85, height=180).apply()
4953

5054
assert u.birthday == now - timedelta(days=3650)
5155
assert u.age == 16
5256
assert u.balance == 100
57+
assert u.height == 180
5358
assert isinstance(u.balance, float)
5459
assert await u.query.gino.model(None).first() == (
5560
1,
5661
"fantix",
5762
dict(age=16, balance=100, birthday=now_str),
63+
dict(bio="I code in Python and more.", height=180),
5864
UserType.USER,
5965
None,
6066
)
@@ -63,12 +69,14 @@ async def test_crud(bind):
6369
# Reload and test updating both JSON and regular property
6470
u = await User.get(u.id)
6571
await u.update(
66-
age=User.age - 2, balance=200.15, realname="daisy", nickname="daisy.nick"
72+
age=User.age - 2, balance=200.15, realname="daisy", nickname="daisy.nick", height=185, weight=75
6773
).apply()
74+
data = await u.query.gino.model(None).first()
6875
assert await u.query.gino.model(None).first() == (
6976
1,
7077
"daisy.nick",
7178
dict(age=14, balance=200, realname="daisy", birthday=now_str),
79+
dict(bio="I code in Python and more.", height=185, weight=75),
7280
UserType.USER,
7381
None,
7482
)
@@ -81,6 +89,9 @@ async def test_crud(bind):
8189
realname="daisy",
8290
type=UserType.USER,
8391
team_id=None,
92+
bio="I code in Python and more.",
93+
height=185,
94+
weight=75
8495
)
8596

8697
# Deleting property doesn't affect database
@@ -130,12 +141,16 @@ class News(db.Model):
130141
# noinspection PyUnusedLocal
131142
async def test_reload(bind):
132143
u = await User.create()
133-
await u.update(realname=db.cast("888", db.Unicode)).apply()
144+
await u.update(realname=db.cast("888", db.Unicode), weight=75).apply()
134145
assert u.realname == "888"
135-
await u.update(profile=None).apply()
146+
assert u.weight == 75
147+
await u.update(profile=None, parameter=None).apply()
136148
assert u.realname == "888"
149+
assert u.weight == 75
137150
User.__dict__["realname"].reload(u)
151+
User.__dict__["weight"].reload(u)
138152
assert u.realname is None
153+
assert u.weight is None
139154

140155

141156
# noinspection PyUnusedLocal
@@ -151,29 +166,60 @@ class PropsTest(db.Model):
151166
obj = db.ObjectProperty()
152167
arr = db.ArrayProperty()
153168

169+
parameter = db.Column(JSONB(), nullable=False, server_default="{}")
170+
171+
raw_param = db.JSONProperty(prop_name='parameter')
172+
bool_param = db.BooleanProperty(prop_name='parameter')
173+
obj_param = db.ObjectProperty(prop_name='parameter')
174+
arr_param = db.ArrayProperty(prop_name='parameter')
175+
154176
await PropsTest.gino.create()
155177
try:
156178
t = await PropsTest.create(
157-
raw=dict(a=[1, 2]), bool=True, obj=dict(x=1, y=2), arr=[3, 4, 5, 6],
179+
raw=dict(a=[1, 2]),
180+
bool=True,
181+
obj=dict(x=1, y=2),
182+
arr=[3, 4, 5, 6],
183+
184+
raw_param=dict(a=[3, 4]),
185+
bool_param=False,
186+
obj_param=dict(x=3, y=4),
187+
arr_param=[7, 8, 9, 10],
158188
)
159189
assert t.obj["x"] == 1
190+
assert t.obj_param["x"] == 3
160191
assert t.arr[-1] == 6
192+
assert t.arr_param[-1] == 10
193+
data = await db.select(
194+
[PropsTest.profile, PropsTest.parameter, PropsTest.raw, PropsTest.bool, PropsTest.obj_param]
195+
).gino.first()
161196
assert await db.select(
162-
[PropsTest.profile, PropsTest.raw, PropsTest.bool,]
197+
[PropsTest.profile, PropsTest.parameter, PropsTest.raw, PropsTest.bool, PropsTest.obj_param]
163198
).gino.first() == (
164199
{
165200
"arr": [3, 4, 5, 6],
166201
"obj": {"x": 1, "y": 2},
167202
"raw": {"a": [1, 2]},
168203
"bool": True,
169204
},
205+
{
206+
"arr_param": [7, 8, 9, 10],
207+
"obj_param": {"x": 3, "y": 4},
208+
"raw_param": {"a": [3, 4]},
209+
"bool_param": False,
210+
},
170211
dict(a=[1, 2]),
171212
True,
213+
dict(x=3, y=4),
172214
)
173215
t.obj = dict(x=10, y=20)
216+
t.obj_param = dict(x=30, y=45)
174217
assert t.obj["x"] == 10
218+
assert t.obj_param["y"] == 45
175219
t.arr = [4, 5, 6, 7]
220+
t.arr_param = [11, 12, 13, 14, 15]
176221
assert t.arr[-1] == 7
222+
assert t.arr_param[-1] == 15
177223
finally:
178224
await PropsTest.gino.drop()
179225

0 commit comments

Comments
 (0)