plain.postgres 0.103.2__tar.gz → 0.103.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/PKG-INFO +1 -1
  2. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/CHANGELOG.md +11 -0
  3. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/related.py +1 -1
  4. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/query.py +7 -1
  5. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/pyproject.toml +1 -1
  6. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_m2m.py +29 -0
  7. plain_postgres-0.103.3/tests/public/test_queryset_repr.py +60 -0
  8. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/.gitignore +0 -0
  9. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/CLAUDE.md +0 -0
  10. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/LICENSE +0 -0
  11. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/README.md +0 -0
  12. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/README.md +0 -0
  13. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/__init__.py +0 -0
  14. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/adapters.py +0 -0
  15. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/agents/.claude/rules/plain-postgres.md +0 -0
  16. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/agents/.claude/skills/plain-postgres-doctor/SKILL.md +0 -0
  17. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/aggregates.py +0 -0
  18. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/base.py +0 -0
  19. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/__init__.py +0 -0
  20. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/converge.py +0 -0
  21. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/core.py +0 -0
  22. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/decorators.py +0 -0
  23. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/diagnose.py +0 -0
  24. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/migrations.py +0 -0
  25. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/schema.py +0 -0
  26. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/cli/sync.py +0 -0
  27. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/config.py +0 -0
  28. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/connection.py +0 -0
  29. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/constants.py +0 -0
  30. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/constraints.py +0 -0
  31. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/convergence/__init__.py +0 -0
  32. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/convergence/analysis.py +0 -0
  33. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/convergence/fixes.py +0 -0
  34. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/convergence/planning.py +0 -0
  35. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/database_url.py +0 -0
  36. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/db.py +0 -0
  37. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/ddl.py +0 -0
  38. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/default_settings.py +0 -0
  39. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/deletion.py +0 -0
  40. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/dialect.py +0 -0
  41. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/entrypoints.py +0 -0
  42. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/enums.py +0 -0
  43. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/exceptions.py +0 -0
  44. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/expressions.py +0 -0
  45. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/__init__.py +0 -0
  46. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/base.py +0 -0
  47. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/binary.py +0 -0
  48. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/boolean.py +0 -0
  49. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/duration.py +0 -0
  50. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/encrypted.py +0 -0
  51. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/json.py +0 -0
  52. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/mixins.py +0 -0
  53. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/network.py +0 -0
  54. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/numeric.py +0 -0
  55. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/primary_key.py +0 -0
  56. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/related_descriptors.py +0 -0
  57. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/related_lookups.py +0 -0
  58. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/related_managers.py +0 -0
  59. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/reverse_descriptors.py +0 -0
  60. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/reverse_related.py +0 -0
  61. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/temporal.py +0 -0
  62. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/text.py +0 -0
  63. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/timezones.py +0 -0
  64. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/fields/uuid.py +0 -0
  65. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/forms.py +0 -0
  66. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/__init__.py +0 -0
  67. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/comparison.py +0 -0
  68. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/datetime.py +0 -0
  69. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/math.py +0 -0
  70. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/mixins.py +0 -0
  71. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/random.py +0 -0
  72. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/text.py +0 -0
  73. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/uuid.py +0 -0
  74. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/functions/window.py +0 -0
  75. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/indexes.py +0 -0
  76. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/__init__.py +0 -0
  77. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/__init__.py +0 -0
  78. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/checks_cumulative.py +0 -0
  79. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/checks_snapshot.py +0 -0
  80. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/checks_structural.py +0 -0
  81. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/context.py +0 -0
  82. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/helpers.py +0 -0
  83. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/ownership.py +0 -0
  84. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/runner.py +0 -0
  85. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/health/types.py +0 -0
  86. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/introspection/schema.py +0 -0
  87. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/lookups.py +0 -0
  88. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/meta.py +0 -0
  89. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/middleware.py +0 -0
  90. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/__init__.py +0 -0
  91. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/autodetector.py +0 -0
  92. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/exceptions.py +0 -0
  93. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/executor.py +0 -0
  94. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/graph.py +0 -0
  95. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/loader.py +0 -0
  96. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/migration.py +0 -0
  97. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/operations/__init__.py +0 -0
  98. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/operations/base.py +0 -0
  99. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/operations/fields.py +0 -0
  100. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/operations/models.py +0 -0
  101. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/operations/special.py +0 -0
  102. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/optimizer.py +0 -0
  103. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/questioner.py +0 -0
  104. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/recorder.py +0 -0
  105. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/serializer.py +0 -0
  106. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/state.py +0 -0
  107. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/utils.py +0 -0
  108. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/migrations/writer.py +0 -0
  109. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/options.py +0 -0
  110. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/otel.py +0 -0
  111. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/preflight.py +0 -0
  112. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/query_utils.py +0 -0
  113. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/registry.py +0 -0
  114. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/schema.py +0 -0
  115. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sources.py +0 -0
  116. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sql/__init__.py +0 -0
  117. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sql/compiler.py +0 -0
  118. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sql/constants.py +0 -0
  119. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sql/datastructures.py +0 -0
  120. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sql/query.py +0 -0
  121. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/sql/where.py +0 -0
  122. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/test/__init__.py +0 -0
  123. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/test/database.py +0 -0
  124. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/test/pytest.py +0 -0
  125. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/transaction.py +0 -0
  126. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/types.py +0 -0
  127. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/types.pyi +0 -0
  128. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/plain/postgres/utils.py +0 -0
  129. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/forms.py +0 -0
  130. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0001_initial.py +0 -0
  131. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
  132. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
  133. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0004_defaultquerysetmodel_mixintestmodel_and_more.py +0 -0
  134. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0005_feature_carfeature_car_features.py +0 -0
  135. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0006_secretstore.py +0 -0
  136. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0007_treenode_unconstrainedchild.py +0 -0
  137. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0008_setsentinelparent_diamondparenta_midparent_and_more.py +0 -0
  138. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0009_circb_circa_circb_partner.py +0 -0
  139. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0010_hideableitem.py +0 -0
  140. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0011_defaultsexample.py +0 -0
  141. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0012_iterationexample.py +0 -0
  142. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0013_indexexample_constraintexample_nullabilityexample.py +0 -0
  143. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0014_widget_rename_feature_tag_remove_carfeature_car_and_more.py +0 -0
  144. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0015_dbdefaultsexample.py +0 -0
  145. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0016_formsexample.py +0 -0
  146. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0017_random_string_token.py +0 -0
  147. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/0018_storageparametersexample.py +0 -0
  148. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/migrations/__init__.py +0 -0
  149. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/__init__.py +0 -0
  150. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/constraints.py +0 -0
  151. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/defaults.py +0 -0
  152. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/delete.py +0 -0
  153. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/encrypted.py +0 -0
  154. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/forms.py +0 -0
  155. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/indexes.py +0 -0
  156. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/iteration.py +0 -0
  157. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/mixins.py +0 -0
  158. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/nullability.py +0 -0
  159. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/querysets.py +0 -0
  160. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/relationships.py +0 -0
  161. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/storage_parameters.py +0 -0
  162. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/trees.py +0 -0
  163. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/models/unregistered.py +0 -0
  164. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/urls.py +0 -0
  165. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/examples/views.py +0 -0
  166. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/settings.py +0 -0
  167. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/app/urls.py +0 -0
  168. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/conftest.py +0 -0
  169. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/conftest_convergence.py +0 -0
  170. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_autodetector_not_null_errors.py +0 -0
  171. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_autodetector_type_change.py +0 -0
  172. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_connection_isolation.py +0 -0
  173. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_connection_lifecycle.py +0 -0
  174. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_connection_pool.py +0 -0
  175. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_constraint_violation_error.py +0 -0
  176. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence.py +0 -0
  177. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_constraints.py +0 -0
  178. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_defaults.py +0 -0
  179. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_fk.py +0 -0
  180. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_indexes.py +0 -0
  181. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_nullability.py +0 -0
  182. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_storage_parameters.py +0 -0
  183. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_convergence_timeouts.py +0 -0
  184. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_db_expression_defaults.py +0 -0
  185. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_diagnose.py +0 -0
  186. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_executor_connection_hook.py +0 -0
  187. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_health.py +0 -0
  188. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_introspection.py +0 -0
  189. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_literal_default_persistence.py +0 -0
  190. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_management_connection.py +0 -0
  191. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_migration_executor.py +0 -0
  192. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_no_callable_defaults.py +0 -0
  193. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_otel_metrics.py +0 -0
  194. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_preflight_fk_coverage.py +0 -0
  195. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_schema_normalize_type.py +0 -0
  196. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/internal/test_schema_timeouts.py +0 -0
  197. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_database_url.py +0 -0
  198. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_delete_behaviors.py +0 -0
  199. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_encrypted_fields.py +0 -0
  200. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_exceptions.py +0 -0
  201. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_field_defaults.py +0 -0
  202. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_functions_uuid.py +0 -0
  203. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_iterator.py +0 -0
  204. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_manager_assignment.py +0 -0
  205. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_mixins.py +0 -0
  206. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_modelform_roundtrip.py +0 -0
  207. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_random_string_field.py +0 -0
  208. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_raw_query.py +0 -0
  209. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_read_only_transactions.py +0 -0
  210. {plain_postgres-0.103.2 → plain_postgres-0.103.3}/tests/public/test_related.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.postgres
3
- Version: 0.103.2
3
+ Version: 0.103.3
4
4
  Summary: Model your data and store it in a database.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-Expression: BSD-3-Clause
@@ -1,5 +1,16 @@
1
1
  # plain-postgres changelog
2
2
 
3
+ ## [0.103.3](https://github.com/dropseed/plain/releases/plain-postgres@0.103.3) (2026-05-08)
4
+
5
+ ### What's changed
6
+
7
+ - **`QuerySet.__repr__` no longer issues SQL for unevaluated querysets.** Error reporters (Sentry, pdb, exception templates) call `repr()` on stack-frame locals to build error events, and a surprise `SELECT … LIMIT 21` inside an exception path is a known footgun — especially on production where the underlying query may itself be the cause of the failure. Unevaluated querysets now render as `<QuerySet [unevaluated]>`; once the result cache is populated, `repr()` formats from the cache as before (still truncating past 20 rows). ([d8b7c4ec30](https://github.com/dropseed/plain/commit/d8b7c4ec30))
8
+ - **`ManyToManyField.value_from_object` no longer calls `.all()` on a manager.** The form-roundtrip path went through `getattr(obj, attname).all()`, which on the M2M manager dispatched to its descriptor and could trigger an unintended fetch/SQL path. Switched to the manager's `.query` queryset, which is the documented entry point. ([83da86b19b](https://github.com/dropseed/plain/commit/83da86b19b))
9
+
10
+ ### Upgrade instructions
11
+
12
+ - No changes required. Note that interactive shell users who relied on `repr()` triggering evaluation (e.g., typing `qs` at the prompt to print rows) will now see `<QuerySet [unevaluated]>` — call `list(qs)` or slice it to materialize.
13
+
3
14
  ## [0.103.2](https://github.com/dropseed/plain/releases/plain-postgres@0.103.2) (2026-05-06)
4
15
 
5
16
  ### What's changed
@@ -1174,7 +1174,7 @@ class ManyToManyField(RelatedField):
1174
1174
  pass
1175
1175
 
1176
1176
  def value_from_object(self, obj: Model) -> list[Any]:
1177
- return [] if obj.id is None else list(getattr(obj, self.attname).all())
1177
+ return [] if obj.id is None else list(getattr(obj, self.attname).query)
1178
1178
 
1179
1179
  def save_form_data(self, instance: Model, data: Any) -> None:
1180
1180
  getattr(instance, self.attname).set(data)
@@ -380,7 +380,13 @@ class QuerySet[T: "Model"]:
380
380
  self.__dict__.update(state)
381
381
 
382
382
  def __repr__(self) -> str:
383
- data: list[Any] = list(self[: REPR_OUTPUT_SIZE + 1])
383
+ # Don't run SQL from __repr__ — error reporters (Sentry, pdb,
384
+ # exception templates) call repr() on stack-frame locals to
385
+ # build error events. If the queryset hasn't been evaluated, a
386
+ # surprise SELECT inside an exception path is a known footgun.
387
+ if self._result_cache is None:
388
+ return f"<{self.__class__.__name__} [unevaluated]>"
389
+ data: list[Any] = list(self._result_cache[: REPR_OUTPUT_SIZE + 1])
384
390
  if len(data) > REPR_OUTPUT_SIZE:
385
391
  data[-1] = "...(remaining elements truncated)..."
386
392
  return f"<{self.__class__.__name__} {data!r}>"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain.postgres"
3
- version = "0.103.2"
3
+ version = "0.103.3"
4
4
  description = "Model your data and store it in a database."
5
5
  authors = [{ name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev" }]
6
6
  readme = "README.md"
@@ -5,10 +5,13 @@ exercise ManyToManyField accessors, the through model, and the Widget-specific
5
5
  unique constraint that produces a realistic ValidationError on duplicate create.
6
6
  """
7
7
 
8
+ from typing import cast
9
+
8
10
  import pytest
9
11
  from app.examples.models.relationships import Tag, Widget, WidgetTag
10
12
 
11
13
  from plain.exceptions import ValidationError
14
+ from plain.postgres.fields.related import ManyToManyField
12
15
 
13
16
 
14
17
  def test_create_unique_constraint(db):
@@ -94,6 +97,32 @@ def test_many_to_many_clear(db):
94
97
  assert widget.tags.query.count() == 0
95
98
 
96
99
 
100
+ def test_value_from_object_returns_related_objects(db):
101
+ """ManyToManyField.value_from_object must return the currently-related
102
+ objects. ModelForm's `model_to_dict` calls this when given an instance
103
+ so the form can populate `initial` for the M2M field — a regression
104
+ here breaks UpdateView for any model with an M2M.
105
+ """
106
+ widget = Widget.query.create(name="Subaru", size="Outback")
107
+ gps = Tag.query.create(name="GPS")
108
+ sunroof = Tag.query.create(name="Sunroof")
109
+ widget.tags.add(gps, sunroof)
110
+
111
+ field = cast(ManyToManyField, Widget._model_meta.get_forward_field("tags"))
112
+ result = field.value_from_object(widget)
113
+
114
+ assert {t.name for t in result} == {"GPS", "Sunroof"}
115
+
116
+
117
+ def test_value_from_object_unsaved_instance_returns_empty(db):
118
+ """An unsaved instance has no related rows; value_from_object should
119
+ return an empty list rather than crash.
120
+ """
121
+ widget = Widget(name="Mazda", size="3")
122
+ field = cast(ManyToManyField, Widget._model_meta.get_forward_field("tags"))
123
+ assert list(field.value_from_object(widget)) == []
124
+
125
+
97
126
  def test_many_to_many_through_model(db):
98
127
  """Test accessing the through model directly."""
99
128
  widget = Widget.query.create(name="Ford", size="Mustang")
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from app.examples.models.iteration import IterationExample
4
+
5
+ from plain.postgres.db import get_connection
6
+
7
+
8
+ def _capture_queries(callable_):
9
+ conn = get_connection()
10
+ prev_force = conn.force_debug_cursor
11
+ conn.force_debug_cursor = True
12
+ conn.queries_log.clear()
13
+ try:
14
+ callable_()
15
+ return list(conn.queries_log)
16
+ finally:
17
+ conn.force_debug_cursor = prev_force
18
+
19
+
20
+ def test_repr_does_not_execute_sql_when_unevaluated(db):
21
+ """repr() of an unevaluated queryset must not issue a SQL query.
22
+
23
+ Error reporters (Sentry, pdb, exception templates) call repr() on
24
+ stack-frame locals; a surprise SELECT inside an exception path can
25
+ overload the database.
26
+ """
27
+ qs = IterationExample.query.all()
28
+
29
+ queries = _capture_queries(lambda: repr(qs))
30
+
31
+ assert queries == []
32
+ assert repr(qs) == "<QuerySet [unevaluated]>"
33
+
34
+
35
+ def test_repr_uses_cache_when_evaluated(db):
36
+ """Once a queryset is evaluated, repr() reflects its rows without re-querying."""
37
+ IterationExample.query.create(name="alpha", tag="a")
38
+ IterationExample.query.create(name="beta", tag="b")
39
+
40
+ qs = IterationExample.query.all()
41
+ list(qs) # force evaluation, populates _result_cache
42
+
43
+ queries = _capture_queries(lambda: repr(qs))
44
+
45
+ assert queries == []
46
+ rendered = repr(qs)
47
+ assert "[unevaluated]" not in rendered
48
+ assert rendered.count("IterationExample") == 2
49
+
50
+
51
+ def test_repr_truncates_large_evaluated_querysets(db):
52
+ """The truncation marker still appears past REPR_OUTPUT_SIZE."""
53
+ for i in range(25):
54
+ IterationExample.query.create(name=f"name{i:02d}", tag="t")
55
+
56
+ qs = IterationExample.query.all()
57
+ list(qs)
58
+
59
+ rendered = repr(qs)
60
+ assert "remaining elements truncated" in rendered