Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update #575

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gel/orm/django/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def table(self):
return self.meta['db_table'].strip("'")

def get_backlink_name(self, name, srcname):
return self.backlink_renames.get(name, f'back_to_{srcname}')
return f'_{name}_{srcname}'


class ModelGenerator(FilePrinter):
Expand Down
29 changes: 2 additions & 27 deletions gel/orm/introspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ def _process_links(types, modules):

objtype = type_map[target]
objtype['backlinks'].append({
'name': f'back_to_{sql_source}',
# naming scheme mimics .<link[is Type]
'name': f'_{sql_name}_{sql_source}',
'fwname': sql_name,
# flip cardinality and exclusivity
'cardinality': 'One' if exclusive else 'Many',
Expand All @@ -198,7 +199,6 @@ def _process_links(types, modules):
'has_link_object': False,
})


link['has_link_object'] = False
# Any link with properties should become its own intermediate
# object, since ORMs generally don't have a special convenient
Expand Down Expand Up @@ -232,31 +232,6 @@ def _process_links(types, modules):
'target': target,
})

# Go over backlinks and resolve any name collisions using the type map.
for spec in types:
mod = spec["name"].rsplit('::', 1)[0]
sql_source = get_sql_name(spec["name"])

# Find collisions in backlink names
bk = collections.defaultdict(list)
for link in spec['backlinks']:
if link['name'].startswith('back_to_'):
bk[link['name']].append(link)

for bklinks in bk.values():
if len(bklinks) > 1:
# We have a collision, so each backlink in it must now be
# disambiguated.
for link in bklinks:
origsrc = get_sql_name(link['target']['name'])
lname = link['name']
fwname = link['fwname']
link['name'] = f'follow_{fwname}_{lname}'
# Also update the original source of the link with the
# special backlink name.
source = type_map[link['target']['name']]
source['backlink_renames'][fwname] = link['name']

return {
'modules': modules,
'object_types': types,
Expand Down
7 changes: 2 additions & 5 deletions gel/orm/sqla.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,7 @@ def render_link_object(self, spec, modules):
bklink = source_link
else:
src = modules[mod]['object_types'][source_name]
bklink = src['backlink_renames'].get(
source_link,
f'back_to_{source_name}',
)
bklink = f'_{source_link}_{source_name}'

self.write(
f'{lname}: orm.Mapped[{pyname}] = '
Expand Down Expand Up @@ -418,7 +415,7 @@ def render_link(self, spec, mod, parent, modules):
tmod, target = get_mod_and_name(spec['target']['name'])
source = modules[mod]['object_types'][parent]
cardinality = spec['cardinality']
bklink = source['backlink_renames'].get(name, f'back_to_{parent}')
bklink = f'_{name}_{parent}'

if spec.get('has_link_object'):
# intermediate object will have the actual source and target
Expand Down
7 changes: 2 additions & 5 deletions gel/orm/sqlmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,7 @@ def render_link_object(self, spec, modules):
bklink = source_link
else:
src = modules[mod]['object_types'][source_name]
bklink = src['backlink_renames'].get(
source_link,
f'back_to_{source_name}',
)
bklink = f'_{source_link}_{source_name}'

self.write(
f'{lname}: {pyname} = sm.Relationship(')
Expand Down Expand Up @@ -452,7 +449,7 @@ def render_link(self, spec, mod, parent, modules):
tmod, target = get_mod_and_name(spec['target']['name'])
source = modules[mod]['object_types'][parent]
cardinality = spec['cardinality']
bklink = source['backlink_renames'].get(name, f'back_to_{parent}')
bklink = f'_{name}_{parent}'

if tmod != 'default':
warnings.warn(
Expand Down
69 changes: 51 additions & 18 deletions tests/test_django_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
NO_ORM = False

from gel import _testbase as tb
from gel.orm.django import generator


class TestDjangoBasic(tb.DjangoTestCase):
Expand Down Expand Up @@ -117,7 +118,7 @@ def test_django_read_models_02(self):
# use backlink
res = self.m.User.objects.order_by('name').all()
vals = [
(u.name, {p.body for p in u.back_to_Post.all()})
(u.name, {p.body for p in u._author_Post.all()})
for u in res
]
self.assertEqual(
Expand Down Expand Up @@ -148,10 +149,10 @@ def test_django_read_models_03(self):
)

# prefetch via backlink
res = self.m.User.objects.prefetch_related('back_to_Post') \
.order_by('back_to_Post__body')
res = self.m.User.objects.prefetch_related('_author_Post') \
.order_by('_author_Post__body')
vals = {
(u.name, tuple(p.body for p in u.back_to_Post.all()))
(u.name, tuple(p.body for p in u._author_Post.all()))
for u in res
}
self.assertEqual(
Expand Down Expand Up @@ -183,7 +184,7 @@ def test_django_read_models_04(self):
# use backlink
res = self.m.User.objects.all()
vals = {
(u.name, tuple(g.num for g in u.back_to_GameSession.all()))
(u.name, tuple(g.num for g in u._players_GameSession.all()))
for u in res
}
self.assertEqual(
Expand Down Expand Up @@ -215,9 +216,9 @@ def test_django_read_models_05(self):
)

# prefetch via backlink
res = self.m.User.objects.prefetch_related('back_to_GameSession')
res = self.m.User.objects.prefetch_related('_players_GameSession')
vals = {
(u.name, tuple(g.num for g in u.back_to_GameSession.all()))
(u.name, tuple(g.num for g in u._players_GameSession.all()))
for u in res
}
self.assertEqual(
Expand Down Expand Up @@ -250,7 +251,7 @@ def test_django_read_models_06(self):
# use backlink
res = self.m.User.objects.order_by('name').all()
vals = [
(u.name, {g.name for g in u.back_to_UserGroup.all()})
(u.name, {g.name for g in u._users_UserGroup.all()})
for u in res
]
self.assertEqual(
Expand Down Expand Up @@ -283,9 +284,9 @@ def test_django_read_models_07(self):
)

# prefetch via backlink
res = self.m.User.objects.prefetch_related('back_to_UserGroup')
res = self.m.User.objects.prefetch_related('_users_UserGroup')
vals = {
(u.name, tuple(sorted(g.name for g in u.back_to_UserGroup.all())))
(u.name, tuple(sorted(g.name for g in u._users_UserGroup.all())))
for u in res
}
self.assertEqual(
Expand Down Expand Up @@ -346,7 +347,7 @@ def test_django_create_models_02(self):
user = self.m.User.objects.get(name=name)

self.assertEqual(user.name, name)
self.assertEqual(user.back_to_UserGroup.all()[0].name, 'cyan')
self.assertEqual(user._users_UserGroup.all()[0].name, 'cyan')
self.assertIsInstance(user.id, uuid.UUID)

def test_django_create_models_03(self):
Expand All @@ -358,8 +359,8 @@ def test_django_create_models_03(self):
y.save()
cyan.save()

x.back_to_UserGroup.add(cyan)
y.back_to_UserGroup.add(cyan)
x._users_UserGroup.add(cyan)
y._users_UserGroup.add(cyan)

group = self.m.UserGroup.objects.get(name='cyan')
self.assertEqual(group.name, 'cyan')
Expand Down Expand Up @@ -442,8 +443,8 @@ def test_django_delete_models_05(self):

group.delete()
# make sure the user object is no longer a link target
user.back_to_UserGroup.clear()
user.back_to_GameSession.clear()
user._users_UserGroup.clear()
user._players_GameSession.clear()
user.delete()

vals = self.m.UserGroup.objects.filter(name='green').all()
Expand Down Expand Up @@ -475,13 +476,13 @@ def test_django_update_models_02(self):
blue.users.add(user)

self.assertEqual(
{g.name for g in user.back_to_UserGroup.all()},
{g.name for g in user._users_UserGroup.all()},
{'red', 'blue'},
)
self.assertEqual(user.name, 'Yvonne')
self.assertIsInstance(user.id, uuid.UUID)

group = [g for g in user.back_to_UserGroup.all()
group = [g for g in user._users_UserGroup.all()
if g.name == 'red'][0]
self.assertEqual(
{u.name for u in group.users.all()},
Expand All @@ -492,7 +493,7 @@ def test_django_update_models_03(self):
user0 = self.m.User.objects.get(name='Elsa')
user1 = self.m.User.objects.get(name='Zoe')
# Replace the author or a post
post = user0.back_to_Post.all()[0]
post = user0._author_Post.all()[0]
body = post.body
post.author = user1
post.save()
Expand Down Expand Up @@ -551,3 +552,35 @@ def test_django_update_models_05(self):
upd.ts,
dt.datetime.fromisoformat('2025-01-20T20:13:45+00:00'),
)

def test_django_sorting(self):
# Test the natural sorting function used for ordering fields, etc.

unsorted = {
'zoo': 1,
'apple': 1,
'potato': 1,
'grape10': 1,
'grape1': 1,
'grape5': 1,
'grape2': 1,
'grape20': 1,
'grape25': 1,
'grape12': 1,
}

self.assertEqual(
list(sorted(unsorted.items(), key=generator.field_name_sort)),
[
('apple', 1),
('grape1', 1),
('grape2', 1),
('grape5', 1),
('grape10', 1),
('grape12', 1),
('grape20', 1),
('grape25', 1),
('potato', 1),
('zoo', 1),
],
)
Loading
Loading