plain.postgres 0.108.1__tar.gz → 0.110.0__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.
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/PKG-INFO +2 -2
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/CHANGELOG.md +29 -2
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/README.md +1 -1
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/base.py +5 -8
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/connection.py +1 -1
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/convergence/analysis.py +163 -86
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/__init__.py +1 -3
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/related.py +34 -174
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/related_descriptors.py +8 -54
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/related_lookups.py +24 -18
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/related_managers.py +32 -43
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/reverse_related.py +7 -88
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/forms.py +1 -1
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/lookups.py +14 -9
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/meta.py +1 -2
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/autodetector.py +4 -11
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/query.py +0 -17
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/query_utils.py +1 -1
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sql/compiler.py +1 -1
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sql/datastructures.py +8 -8
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sql/query.py +10 -13
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/types.pyi +0 -9
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/pyproject.toml +1 -1
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0007_treenode_unconstrainedchild.py +0 -18
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/delete.py +0 -11
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_constraints.py +112 -47
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_fk.py +1 -15
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_indexes.py +20 -20
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_literal_default_persistence.py +5 -5
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_delete_behaviors.py +0 -24
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_related.py +0 -27
- plain_postgres-0.110.0/tests/public/test_related_instance_filter.py +89 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/.gitignore +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/CLAUDE.md +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/LICENSE +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/README.md +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/adapters.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/agents/.claude/rules/plain-postgres.md +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/agents/.claude/skills/plain-postgres-doctor/SKILL.md +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/aggregates.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/converge.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/core.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/decorators.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/diagnose.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/migrations.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/schema.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/cli/sync.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/config.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/constants.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/constraints.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/convergence/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/convergence/fixes.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/convergence/planning.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/database_url.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/db.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/ddl.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/default_settings.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/deletion.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/dialect.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/entrypoints.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/enums.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/exceptions.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/expressions.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/base.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/binary.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/boolean.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/duration.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/encrypted.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/json.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/mixins.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/network.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/numeric.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/primary_key.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/reverse_descriptors.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/temporal.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/text.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/timezones.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/fields/uuid.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/comparison.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/datetime.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/math.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/mixins.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/random.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/text.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/uuid.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/functions/window.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/indexes.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/checks_cumulative.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/checks_snapshot.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/checks_structural.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/context.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/helpers.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/ownership.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/runner.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/health/types.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/introspection/schema.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/middleware.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/exceptions.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/executor.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/graph.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/loader.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/migration.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/operations/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/operations/base.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/operations/fields.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/operations/models.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/operations/special.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/optimizer.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/questioner.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/recorder.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/serializer.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/state.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/utils.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/migrations/writer.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/options.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/otel.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/preflight/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/preflight/database.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/preflight/indexes.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/preflight/models.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/registry.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/schema.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sources.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sql/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sql/constants.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/sql/where.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/test/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/test/database.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/test/pytest.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/transaction.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/types.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/plain/postgres/utils.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/forms.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0001_initial.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0004_defaultquerysetmodel_mixintestmodel_and_more.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0005_feature_carfeature_car_features.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0006_secretstore.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0008_setsentinelparent_diamondparenta_midparent_and_more.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0009_circb_circa_circb_partner.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0010_hideableitem.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0011_defaultsexample.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0012_iterationexample.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0013_indexexample_constraintexample_nullabilityexample.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0014_widget_rename_feature_tag_remove_carfeature_car_and_more.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0015_dbdefaultsexample.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0016_formsexample.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0017_random_string_token.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/0018_storageparametersexample.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/migrations/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/__init__.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/constraints.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/defaults.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/encrypted.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/forms.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/indexes.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/iteration.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/mixins.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/nullability.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/querysets.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/relationships.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/storage_parameters.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/trees.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/models/unregistered.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/urls.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/examples/views.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/settings.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/app/urls.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/conftest.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/conftest_convergence.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_autodetector_not_null_errors.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_autodetector_type_change.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_connection_isolation.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_connection_lifecycle.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_connection_pool.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_connection_self_heal.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_constraint_violation_error.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_defaults.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_nullability.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_storage_parameters.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_convergence_timeouts.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_db_expression_defaults.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_diagnose.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_executor_connection_hook.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_fk_characterization.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_health.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_introspection.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_management_connection.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_migration_executor.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_no_callable_defaults.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_otel_metrics.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_preflight_duplicate_indexes.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_preflight_fk_composite_hint.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_preflight_fk_coverage.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_schema_normalize_type.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/internal/test_schema_timeouts.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_create_update.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_database_url.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_deferred_loading.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_encrypted_fields.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_exceptions.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_field_defaults.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_functions_uuid.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_integrity_error_mapping.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_iterator.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_m2m.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_manager_assignment.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_manual_pk.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_mixins.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_modelform_roundtrip.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_queryset_repr.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_random_string_field.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_raw_query.py +0 -0
- {plain_postgres-0.108.1 → plain_postgres-0.110.0}/tests/public/test_read_only_transactions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plain.postgres
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.110.0
|
|
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
|
|
@@ -957,7 +957,7 @@ book.author.name # one query — loads the rest of the row
|
|
|
957
957
|
|
|
958
958
|
The first access to any non-key field loads the whole row in a single query. There is no separate `author_id` attribute — `book.author.id` is the foreign key value, and it is type-checked because `book.author` is an `Author`. In loops, use `select_related()` to load related rows up front and avoid a query per row.
|
|
959
959
|
|
|
960
|
-
The partial-instance shortcut
|
|
960
|
+
The partial-instance shortcut is safe because Plain always creates a database foreign-key constraint, so the referenced row is guaranteed to exist.
|
|
961
961
|
|
|
962
962
|
### Reverse relationships
|
|
963
963
|
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# plain-postgres changelog
|
|
2
2
|
|
|
3
|
+
## [0.110.0](https://github.com/dropseed/plain/releases/plain-postgres@0.110.0) (2026-06-17)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- Internal: the foreign-key/related-field internals are collapsed to the single-column-to-`id` model Plain actually uses, removing the inherited Django composite-key and `to_field` scaffolding. `PathInfo.target_fields` (a tuple) becomes a scalar `target_field`, and a `Join` now holds one `join_col` pair instead of a `join_cols` list. The unused multi-column helpers go with it: `RelatedField.resolve_related_fields()`, the `related_fields`/`local_related_fields`/`foreign_related_fields` properties, `ForeignKeyField.get_reverse_joining_columns()`, and `ForeignObjectRel.get_related_field()` / `field_name` / `set_field_name()` / `db_type` / the `multiple` flag (along with its now-dead reverse-cache priming, left over from the long-removed one-to-one relation). `get_local_related_value()`, `get_foreign_related_value()`, and the join-column accessors now return scalars instead of 1-tuples. ([cbd15fb707](https://github.com/dropseed/plain/commit/cbd15fb707))
|
|
8
|
+
- Internal: the related-lookup prep path (`related_lookups.py`, `lookups.py`) now narrows on `RelatedField | ForeignObjectRel` to find a relation's target field instead of duck-typing with `getattr(output_field, "target_field")` / `hasattr(..., "path_infos")`, so the dependency is greppable, type-checked, and fails loudly if it's ever removed rather than silently falling back. ([cbd15fb707](https://github.com/dropseed/plain/commit/cbd15fb707))
|
|
9
|
+
|
|
10
|
+
### Upgrade instructions
|
|
11
|
+
|
|
12
|
+
- No changes required. These are internal ORM query-construction details — how foreign keys, reverse relations, and many-to-many relations behave (filtering, prefetching, related-manager access) is unchanged.
|
|
13
|
+
|
|
14
|
+
## [0.109.0](https://github.com/dropseed/plain/releases/plain-postgres@0.109.0) (2026-06-17)
|
|
15
|
+
|
|
16
|
+
### What's changed
|
|
17
|
+
|
|
18
|
+
- **The `db_constraint` argument is removed from `ForeignKeyField` — every foreign key is now backed by a database `FOREIGN KEY` constraint, always.** Because the database guarantees the referenced row exists, accessing a foreign key always builds a partial related instance from just the stored id (no query); the old "query on access when unconstrained" path is gone. The `fields.foreign_key_unconstrained_requires_no_action` preflight check is also removed. ([9ec559db38](https://github.com/dropseed/plain/commit/9ec559db38))
|
|
19
|
+
- **`limit_choices_to` is removed from `ForeignKeyField` and `ManyToManyField`.** The supporting machinery goes with it: `RelatedField.get_choices()` / `get_limit_choices_to()`, `ForeignObjectRel.get_choices()`, and `QuerySet.complex_filter()` are removed, and `BLANK_CHOICE_DASH` is no longer exported from `plain.postgres.fields`. To restrict the rows offered for a relation, filter the queryset explicitly in your form or view. ([68b6ced4f0](https://github.com/dropseed/plain/commit/68b6ced4f0))
|
|
20
|
+
- **The redundant foreign-key existence pre-check is removed.** `ForeignKeyField.validate()` no longer runs a `SELECT ... EXISTS` query to confirm the target row exists — one fewer query per FK validation. The database `FOREIGN KEY` constraint already enforces this, rejecting an invalid reference at write time (as a `psycopg.IntegrityError`). ([68b6ced4f0](https://github.com/dropseed/plain/commit/68b6ced4f0))
|
|
21
|
+
- Internal: convergence (schema-drift) analysis now reuses a single session-private temp "probe" table per model across all of that model's round-trip comparisons, instead of creating and dropping one per index/constraint/default comparison — fewer DDL statements during schema checks. The internal `canon*` helpers were renamed to `probe`/`normalize`. ([67167a11ee](https://github.com/dropseed/plain/commit/67167a11ee), [06650bea63](https://github.com/dropseed/plain/commit/06650bea63))
|
|
22
|
+
|
|
23
|
+
### Upgrade instructions
|
|
24
|
+
|
|
25
|
+
- Remove any `db_constraint=False` arguments from `ForeignKeyField`. Foreign keys are always DB-constrained now; an unconstrained FK (e.g. for soft/cross-system references) is no longer supported.
|
|
26
|
+
- Remove `limit_choices_to=` from `ForeignKeyField` / `ManyToManyField` definitions. To constrain the rows offered for a relation, filter the related queryset explicitly in your form or view.
|
|
27
|
+
- Replace any `QuerySet.complex_filter(...)` calls with `.filter(...)` — pass a dict of lookups or a `Q` object directly.
|
|
28
|
+
- Most projects use none of these and need no changes.
|
|
29
|
+
|
|
3
30
|
## [0.108.1](https://github.com/dropseed/plain/releases/plain-postgres@0.108.1) (2026-06-09)
|
|
4
31
|
|
|
5
32
|
### What's changed
|
|
@@ -41,7 +68,7 @@
|
|
|
41
68
|
|
|
42
69
|
### Upgrade instructions
|
|
43
70
|
|
|
44
|
-
- Replace `form.save()` with `form.create()` in create flows and `form.update()` in update flows.
|
|
71
|
+
- Replace `form.save()` with `form.create()` in create flows and `form.update()` in update flows.
|
|
45
72
|
- `form.update(fields=[...])` is available to limit the columns written (a pass-through to `Model.update(fields=...)`).
|
|
46
73
|
- For the old `form.save(commit=False)` pattern (set extra non-form attributes, then save), assign those attributes on `form.instance` after `is_valid()`, then call `form.create()` / `form.update()` — m2m data is still saved for you, so the separate `save_m2m()` step is no longer needed:
|
|
47
74
|
|
|
@@ -63,7 +90,7 @@
|
|
|
63
90
|
|
|
64
91
|
### Upgrade instructions
|
|
65
92
|
|
|
66
|
-
- Replace `obj.save()` with `obj.create()` for a new row or `obj.update()` for an existing one; `obj.save(update_fields=[...])` becomes `obj.update(fields=[...])`.
|
|
93
|
+
- Replace `obj.save()` with `obj.create()` for a new row or `obj.update()` for an existing one; `obj.save(update_fields=[...])` becomes `obj.update(fields=[...])`.
|
|
67
94
|
- Remove `force_insert=`/`force_update=` — `create()` and `update()` are the explicit forms.
|
|
68
95
|
- Don't pass `id=` to a model constructor; load existing rows with `Model.query.get(id=...)`.
|
|
69
96
|
- Catch `ValidationError` (or `(psycopg.IntegrityError, ValidationError)` when retrying) for unique/check conflicts on instance writes. Inside an open `transaction.atomic()` a violation aborts the transaction — wrap the write in its own `atomic()` to catch and continue.
|
|
@@ -944,7 +944,7 @@ book.author.name # one query — loads the rest of the row
|
|
|
944
944
|
|
|
945
945
|
The first access to any non-key field loads the whole row in a single query. There is no separate `author_id` attribute — `book.author.id` is the foreign key value, and it is type-checked because `book.author` is an `Author`. In loops, use `select_related()` to load related rows up front and avoid a query per row.
|
|
946
946
|
|
|
947
|
-
The partial-instance shortcut
|
|
947
|
+
The partial-instance shortcut is safe because Plain always creates a database foreign-key constraint, so the referenced row is guaranteed to exist.
|
|
948
948
|
|
|
949
949
|
### Reverse relationships
|
|
950
950
|
|
|
@@ -538,9 +538,6 @@ class Model(metaclass=ModelBase):
|
|
|
538
538
|
# constraints aren't supported by the database, there's the
|
|
539
539
|
# unavoidable risk of data corruption.
|
|
540
540
|
if obj.id is None:
|
|
541
|
-
# Remove the object from a related instance cache.
|
|
542
|
-
if not field.remote_field.multiple:
|
|
543
|
-
field.remote_field.delete_cached_value(obj)
|
|
544
541
|
raise ValueError(
|
|
545
542
|
f"{operation_name}() prohibited to prevent data loss due to unsaved "
|
|
546
543
|
f"related object '{field.name}'."
|
|
@@ -549,10 +546,10 @@ class Model(metaclass=ModelBase):
|
|
|
549
546
|
# Set related object if it has been saved after an
|
|
550
547
|
# assignment.
|
|
551
548
|
setattr(self, field.name, obj)
|
|
552
|
-
# If the relationship's
|
|
553
|
-
#
|
|
554
|
-
#
|
|
555
|
-
#
|
|
549
|
+
# If the relationship's key was changed, clear the cached
|
|
550
|
+
# relationship. Compare the cached object's key against the raw
|
|
551
|
+
# key value -- not getattr(self, field.name), which for a
|
|
552
|
+
# foreign key returns the related object, not the key.
|
|
556
553
|
if getattr(obj, field.target_field.name) != field.value_from_object(
|
|
557
554
|
self
|
|
558
555
|
):
|
|
@@ -622,7 +619,7 @@ class Model(metaclass=ModelBase):
|
|
|
622
619
|
raise ValueError(
|
|
623
620
|
f"Unsaved model instance {self!r} cannot be used in an ORM query."
|
|
624
621
|
)
|
|
625
|
-
return getattr(self, field.
|
|
622
|
+
return getattr(self, field.target_field.name)
|
|
626
623
|
|
|
627
624
|
def clean(self) -> None:
|
|
628
625
|
"""
|
|
@@ -646,7 +646,7 @@ class DatabaseConnection:
|
|
|
646
646
|
}
|
|
647
647
|
# Now get indexes. Sort order, opclasses, INCLUDE, and predicates all
|
|
648
648
|
# ride along inside `pg_get_indexdef` and are compared via the
|
|
649
|
-
#
|
|
649
|
+
# normalized-tail round-trip in convergence — no need to introspect
|
|
650
650
|
# them here as separate columns.
|
|
651
651
|
cursor.execute(
|
|
652
652
|
"""
|