Skip to content

Commit 59f52b8

Browse files
committed
refactor(crud): Remove .c accessor for direct field access
BREAKING CHANGE (In PR level): Remove Entity.c / Relation.c accessor in favor of direct class-level field access for query building. Before: Person.c.age.gt(Age(30)) After: Person.age.gt(Age(30)) This improves DX by: - Enabling IDE autocomplete for field names (real class attributes) - Providing type-safe method hints via FieldRef @overload signatures - Removing cognitive overhead of the .c indirection Implementation: - Add __pydantic_init_subclass__ to TypeDBType that injects FieldDescriptor instances after Pydantic completes model initialization - Remove c: ClassVar[Any] declaration and FieldsAccessor initialization - Delete type_bridge/models/fields_accessor.py - Update all .c. usages in tests and docstrings
1 parent aee1fb4 commit 59f52b8

File tree

8 files changed

+162
-203
lines changed

8 files changed

+162
-203
lines changed

tests/integration/queries/test_expressions.py

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def test_greater_than_filter(setup_schema):
170170
manager = Person.manager(setup_schema)
171171

172172
# age > 30
173-
results = manager.filter(Person.c.age.gt(PersonAge(30))).execute()
173+
results = manager.filter(Person.age.gt(PersonAge(30))).execute()
174174

175175
assert len(results) == 3 # Bob(35), Charlie(45), Eve(52) - Frank(30) excluded
176176
ages = [p.age.value for p in results]
@@ -184,7 +184,7 @@ def test_less_than_filter(setup_schema):
184184
manager = Person.manager(setup_schema)
185185

186186
# age < 30
187-
results = manager.filter(Person.c.age.lt(PersonAge(30))).execute()
187+
results = manager.filter(Person.age.lt(PersonAge(30))).execute()
188188

189189
assert len(results) == 2 # Alice(25), Diana(28)
190190
ages = [p.age.value for p in results]
@@ -198,9 +198,7 @@ def test_range_query(setup_schema):
198198
manager = Person.manager(setup_schema)
199199

200200
# 28 <= age <= 35
201-
results = manager.filter(
202-
Person.c.age.gte(PersonAge(28)), Person.c.age.lte(PersonAge(35))
203-
).execute()
201+
results = manager.filter(Person.age.gte(PersonAge(28)), Person.age.lte(PersonAge(35))).execute()
204202

205203
assert len(results) == 3 # Diana(28), Frank(30), Bob(35)
206204
ages = [p.age.value for p in results]
@@ -214,7 +212,7 @@ def test_equality_filter(setup_schema):
214212
manager = Person.manager(setup_schema)
215213

216214
# age == 35
217-
results = manager.filter(Person.c.age.eq(PersonAge(35))).execute()
215+
results = manager.filter(Person.age.eq(PersonAge(35))).execute()
218216

219217
assert len(results) == 1
220218
assert results[0].age.value == 35
@@ -228,7 +226,7 @@ def test_not_equal_filter(setup_schema):
228226
manager = Person.manager(setup_schema)
229227

230228
# age != 35
231-
results = manager.filter(Person.c.age.neq(PersonAge(35))).execute()
229+
results = manager.filter(Person.age.neq(PersonAge(35))).execute()
232230

233231
assert len(results) == 5 # All except Bob
234232
ages = [p.age.value for p in results]
@@ -247,7 +245,7 @@ def test_contains_filter(setup_schema):
247245
manager = Person.manager(setup_schema)
248246

249247
# email contains '@company.com'
250-
results = manager.filter(Person.c.email.contains(PersonEmail("@company.com"))).execute()
248+
results = manager.filter(Person.email.contains(PersonEmail("@company.com"))).execute()
251249

252250
assert len(results) == 4 # All except Charlie and Frank (gmail)
253251
for person in results:
@@ -261,7 +259,7 @@ def test_like_regex_filter(setup_schema):
261259
manager = Person.manager(setup_schema)
262260

263261
# name starts with 'A' or 'B'
264-
results = manager.filter(Person.c.name.like(PersonName("^[AB].*"))).execute()
262+
results = manager.filter(Person.name.like(PersonName("^[AB].*"))).execute()
265263

266264
assert len(results) == 2 # Alice, Bob
267265
for person in results:
@@ -281,7 +279,7 @@ def test_or_logic(setup_schema):
281279

282280
# age < 28 OR age > 45
283281
results = manager.filter(
284-
Person.c.age.lt(PersonAge(28)).or_(Person.c.age.gt(PersonAge(45)))
282+
Person.age.lt(PersonAge(28)).or_(Person.age.gt(PersonAge(45)))
285283
).execute()
286284

287285
assert len(results) == 2 # Alice(25), Eve(52)
@@ -297,7 +295,7 @@ def test_and_logic_explicit(setup_schema):
297295

298296
# Engineering AND age > 40
299297
results = manager.filter(
300-
Person.c.department.eq(PersonDepartment("Engineering")).and_(Person.c.age.gt(PersonAge(40)))
298+
Person.department.eq(PersonDepartment("Engineering")).and_(Person.age.gt(PersonAge(40)))
301299
).execute()
302300

303301
assert len(results) == 1 # Eve(52, Engineering)
@@ -314,7 +312,7 @@ def test_and_logic_implicit(setup_schema):
314312

315313
# Engineering AND age > 30 (implicit AND)
316314
results = manager.filter(
317-
Person.c.department.eq(PersonDepartment("Engineering")), Person.c.age.gt(PersonAge(30))
315+
Person.department.eq(PersonDepartment("Engineering")), Person.age.gt(PersonAge(30))
318316
).execute()
319317

320318
assert len(results) == 2 # Bob(35), Eve(52)
@@ -330,9 +328,7 @@ def test_not_logic(setup_schema):
330328
manager = Person.manager(setup_schema)
331329

332330
# NOT Engineering
333-
results = manager.filter(
334-
Person.c.department.eq(PersonDepartment("Engineering")).not_()
335-
).execute()
331+
results = manager.filter(Person.department.eq(PersonDepartment("Engineering")).not_()).execute()
336332

337333
assert len(results) == 3 # Charlie, Diana, Frank (not Engineering)
338334
for person in results:
@@ -347,8 +343,8 @@ def test_complex_boolean(setup_schema):
347343

348344
# (age > 40 AND salary > 100k) OR score > 95
349345
results = manager.filter(
350-
Person.c.age.gt(PersonAge(40))
351-
.and_(Person.c.salary.gt(PersonSalary(100000.0)))
346+
Person.age.gt(PersonAge(40))
347+
.and_(Person.salary.gt(PersonSalary(100000.0)))
352348
.or_(PersonScore.gt(PersonScore(95.0)))
353349
).execute()
354350

@@ -372,7 +368,7 @@ def test_single_aggregation(setup_schema):
372368
manager = Person.manager(setup_schema)
373369

374370
# Average age
375-
result = manager.filter().aggregate(Person.c.age.avg())
371+
result = manager.filter().aggregate(Person.age.avg())
376372

377373
assert "avg_age" in result
378374
# Average of [25, 35, 45, 28, 52, 30] = 35.83...
@@ -387,11 +383,11 @@ def test_multiple_aggregations(setup_schema):
387383

388384
# Multiple stats
389385
result = manager.filter().aggregate(
390-
Person.c.age.avg(),
391-
Person.c.salary.avg(),
392-
Person.c.salary.sum(),
393-
Person.c.salary.max(),
394-
Person.c.salary.min(),
386+
Person.age.avg(),
387+
Person.salary.avg(),
388+
Person.salary.sum(),
389+
Person.salary.max(),
390+
Person.salary.min(),
395391
)
396392

397393
assert "avg_age" in result
@@ -413,8 +409,8 @@ def test_filtered_aggregation(setup_schema):
413409
manager = Person.manager(setup_schema)
414410

415411
# Average salary for Engineering only
416-
result = manager.filter(Person.c.department.eq(PersonDepartment("Engineering"))).aggregate(
417-
Person.c.salary.avg()
412+
result = manager.filter(Person.department.eq(PersonDepartment("Engineering"))).aggregate(
413+
Person.salary.avg()
418414
)
419415

420416
assert "avg_salary" in result
@@ -433,10 +429,8 @@ def test_group_by_single_field(setup_schema):
433429
"""Test grouping by single field."""
434430
manager = Person.manager(setup_schema)
435431

436-
# Group by department using FieldsAccessor (Person.c.field)
437-
result = manager.group_by(Person.c.department).aggregate(
438-
Person.c.age.avg(), Person.c.salary.avg()
439-
)
432+
# Group by department using direct field access (Person.field)
433+
result = manager.group_by(Person.department).aggregate(Person.age.avg(), Person.salary.avg())
440434

441435
# Should have 3 departments
442436
assert len(result) == 3
@@ -460,9 +454,9 @@ def test_group_by_with_filter(setup_schema):
460454

461455
# Group by department, only age > 30
462456
result = (
463-
manager.filter(Person.c.age.gt(PersonAge(30)))
464-
.group_by(Person.c.department)
465-
.aggregate(Person.c.salary.avg())
457+
manager.filter(Person.age.gt(PersonAge(30)))
458+
.group_by(Person.department)
459+
.aggregate(Person.salary.avg())
466460
)
467461

468462
# Should have groups, but filtered
@@ -480,8 +474,8 @@ def test_group_by_multiple_fields(setup_schema):
480474
"""Test grouping by multiple fields."""
481475
manager = Person.manager(setup_schema)
482476

483-
# Group by city AND department using FieldsAccessor
484-
result = manager.group_by(Person.c.city, Person.c.department).aggregate(Person.c.age.avg())
477+
# Group by city AND department using direct field access
478+
result = manager.group_by(Person.city, Person.department).aggregate(Person.age.avg())
485479

486480
# Should have tuple keys
487481
assert len(result) >= 3
@@ -533,7 +527,7 @@ def test_first(setup_schema):
533527
manager = Person.manager(setup_schema)
534528

535529
# Get first Engineering employee
536-
first = manager.filter(Person.c.department.eq(PersonDepartment("Engineering"))).first()
530+
first = manager.filter(Person.department.eq(PersonDepartment("Engineering"))).first()
537531

538532
assert first is not None
539533
assert first.department.value == "Engineering"
@@ -546,7 +540,7 @@ def test_count(setup_schema):
546540
manager = Person.manager(setup_schema)
547541

548542
# Count Engineering employees
549-
count = manager.filter(Person.c.department.eq(PersonDepartment("Engineering"))).count()
543+
count = manager.filter(Person.department.eq(PersonDepartment("Engineering"))).count()
550544

551545
assert count == 3 # Alice, Bob, Eve
552546

@@ -566,8 +560,8 @@ def test_complex_query_promotion_candidates(setup_schema):
566560
# score > 90 AND salary < 100k AND age < 50
567561
candidates = manager.filter(
568562
PersonScore.gt(PersonScore(90.0)),
569-
Person.c.salary.lt(PersonSalary(100000.0)),
570-
Person.c.age.lt(PersonAge(50)),
563+
Person.salary.lt(PersonSalary(100000.0)),
564+
Person.age.lt(PersonAge(50)),
571565
).execute()
572566

573567
# Should find Alice(92.5, 75k, 25) - Frank(90, 82k, 30) excluded (score == 90, not > 90)
@@ -587,7 +581,7 @@ def test_complex_query_with_aggregation(setup_schema):
587581

588582
# High performers' average salary
589583
result = manager.filter(PersonScore.gt(PersonScore(90.0))).aggregate(
590-
Person.c.salary.avg(), Person.c.age.avg()
584+
Person.salary.avg(), Person.age.avg()
591585
)
592586

593587
assert "avg_salary" in result
@@ -620,7 +614,7 @@ def test_mixed_dict_and_expression_filters(setup_schema):
620614

621615
# Mix old and new style
622616
results = manager.filter(
623-
Person.c.age.gt(PersonAge(30)), # Expression
617+
Person.age.gt(PersonAge(30)), # Expression
624618
department="Engineering", # Dict filter
625619
).execute()
626620

tests/unit/exceptions/test_exceptions.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,8 @@ class BoolTestPerson(Entity):
312312
name: BoolTestName = Flag(Key)
313313
age: BoolTestAge
314314

315-
expr1 = BoolTestPerson.c.age.gt(BoolTestAge(18))
316-
expr2 = BoolTestPerson.c.age.lt(BoolTestAge(65))
315+
expr1 = BoolTestPerson.age.gt(BoolTestAge(18))
316+
expr2 = BoolTestPerson.age.lt(BoolTestAge(65))
317317

318318
with pytest.raises(ValueError, match="NOT operation requires exactly 1 operand"):
319319
BooleanExpr("not", [expr1, expr2])
@@ -335,7 +335,7 @@ class AndTestPerson(Entity):
335335
name: AndTestName = Flag(Key)
336336
age: AndTestAge
337337

338-
expr = AndTestPerson.c.age.gt(AndTestAge(18))
338+
expr = AndTestPerson.age.gt(AndTestAge(18))
339339

340340
with pytest.raises(ValueError, match="AND operation requires at least 2 operands"):
341341
BooleanExpr("and", [expr])
@@ -357,7 +357,7 @@ class OrTestPerson(Entity):
357357
name: OrTestName = Flag(Key)
358358
age: OrTestAge
359359

360-
expr = OrTestPerson.c.age.gt(OrTestAge(18))
360+
expr = OrTestPerson.age.gt(OrTestAge(18))
361361

362362
with pytest.raises(ValueError, match="OR operation requires at least 2 operands"):
363363
BooleanExpr("or", [expr])
@@ -379,8 +379,8 @@ class AndOkPerson(Entity):
379379
name: AndOkName = Flag(Key)
380380
age: AndOkAge
381381

382-
expr1 = AndOkPerson.c.age.gt(AndOkAge(18))
383-
expr2 = AndOkPerson.c.age.lt(AndOkAge(65))
382+
expr1 = AndOkPerson.age.gt(AndOkAge(18))
383+
expr2 = AndOkPerson.age.lt(AndOkAge(65))
384384

385385
# Should not raise
386386
result = BooleanExpr("and", [expr1, expr2])
@@ -404,7 +404,7 @@ class NotOkPerson(Entity):
404404
name: NotOkName = Flag(Key)
405405
age: NotOkAge
406406

407-
expr = NotOkPerson.c.age.eq(NotOkAge(30))
407+
expr = NotOkPerson.age.eq(NotOkAge(30))
408408

409409
# Should not raise
410410
result = BooleanExpr("not", [expr])

0 commit comments

Comments
 (0)