plain.postgres 0.106.0__tar.gz → 0.107.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.106.0 → plain_postgres-0.107.0}/PKG-INFO +3 -3
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/CHANGELOG.md +18 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/README.md +2 -2
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/forms.py +28 -21
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/pyproject.toml +1 -1
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/urls.py +5 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/views.py +8 -1
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_modelform_roundtrip.py +28 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/.gitignore +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/CLAUDE.md +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/LICENSE +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/README.md +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/adapters.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/agents/.claude/rules/plain-postgres.md +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/agents/.claude/skills/plain-postgres-doctor/SKILL.md +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/aggregates.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/base.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/converge.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/core.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/decorators.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/diagnose.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/migrations.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/schema.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/cli/sync.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/config.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/connection.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/constants.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/constraints.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/convergence/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/convergence/analysis.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/convergence/fixes.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/convergence/planning.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/database_url.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/db.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/ddl.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/default_settings.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/deletion.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/dialect.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/entrypoints.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/enums.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/exceptions.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/expressions.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/base.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/binary.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/boolean.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/duration.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/encrypted.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/json.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/mixins.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/network.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/numeric.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/primary_key.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/related.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/related_descriptors.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/related_lookups.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/related_managers.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/reverse_descriptors.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/reverse_related.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/temporal.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/text.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/timezones.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/uuid.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/comparison.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/datetime.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/math.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/mixins.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/random.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/text.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/uuid.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/functions/window.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/indexes.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/checks_cumulative.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/checks_snapshot.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/checks_structural.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/context.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/helpers.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/ownership.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/runner.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/types.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/schema.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/lookups.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/meta.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/middleware.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/autodetector.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/exceptions.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/executor.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/graph.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/loader.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/migration.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/base.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/fields.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/models.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/special.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/optimizer.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/questioner.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/recorder.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/serializer.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/state.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/utils.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/writer.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/options.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/otel.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/preflight.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/query.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/query_utils.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/registry.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/schema.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sources.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sql/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sql/compiler.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sql/constants.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sql/datastructures.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sql/query.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/sql/where.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/test/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/test/database.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/test/pytest.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/transaction.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/types.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/types.pyi +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/utils.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/forms.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0001_initial.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0002_test_field_removed.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0003_deleteparent_childsetnull_childsetdefault_and_more.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0004_defaultquerysetmodel_mixintestmodel_and_more.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0005_feature_carfeature_car_features.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0006_secretstore.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0007_treenode_unconstrainedchild.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0008_setsentinelparent_diamondparenta_midparent_and_more.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0009_circb_circa_circb_partner.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0010_hideableitem.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0011_defaultsexample.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0012_iterationexample.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0013_indexexample_constraintexample_nullabilityexample.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0014_widget_rename_feature_tag_remove_carfeature_car_and_more.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0015_dbdefaultsexample.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0016_formsexample.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0017_random_string_token.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0018_storageparametersexample.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/__init__.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/constraints.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/defaults.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/delete.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/encrypted.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/forms.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/indexes.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/iteration.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/mixins.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/nullability.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/querysets.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/relationships.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/storage_parameters.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/trees.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/unregistered.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/settings.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/urls.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/conftest.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/conftest_convergence.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_autodetector_not_null_errors.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_autodetector_type_change.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_connection_isolation.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_connection_lifecycle.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_connection_pool.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_constraint_violation_error.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_constraints.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_defaults.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_fk.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_indexes.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_nullability.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_storage_parameters.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_timeouts.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_db_expression_defaults.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_diagnose.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_executor_connection_hook.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_fk_characterization.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_health.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_introspection.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_literal_default_persistence.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_management_connection.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_migration_executor.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_no_callable_defaults.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_otel_metrics.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_preflight_duplicate_indexes.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_preflight_fk_coverage.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_schema_normalize_type.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_schema_timeouts.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_create_update.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_database_url.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_deferred_loading.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_delete_behaviors.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_encrypted_fields.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_exceptions.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_field_defaults.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_functions_uuid.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_integrity_error_mapping.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_iterator.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_m2m.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_manager_assignment.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_manual_pk.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_mixins.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_queryset_repr.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_random_string_field.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_raw_query.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_read_only_transactions.py +0 -0
- {plain_postgres-0.106.0 → plain_postgres-0.107.0}/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.
|
|
3
|
+
Version: 0.107.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
|
|
@@ -1066,7 +1066,7 @@ except ValidationError:
|
|
|
1066
1066
|
... # report it — the transaction is still usable
|
|
1067
1067
|
```
|
|
1068
1068
|
|
|
1069
|
-
Forms are the exception: a `ModelForm` pre-checks constraints explicitly (a `validate_constraints()` call in its `_post_clean`) so it can surface every violation at once, then
|
|
1069
|
+
Forms are the exception: a `ModelForm` pre-checks constraints explicitly (a `validate_constraints()` call in its `_post_clean`) so it can surface every violation at once, then writes via `form.create()`/`form.update()` with validation already done. A direct `create()`/`update()` reports the first violation Postgres hits.
|
|
1070
1070
|
|
|
1071
1071
|
This applies to instance writes only. Set-based writes — `QuerySet.update()` and `bulk_create()` — raise the raw `psycopg.IntegrityError`, since there's no instance to attribute the error to. If you retry on a unique conflict, catch both:
|
|
1072
1072
|
|
|
@@ -1234,7 +1234,7 @@ class UserForm(forms.ModelForm):
|
|
|
1234
1234
|
# Usage
|
|
1235
1235
|
form = UserForm(request=request)
|
|
1236
1236
|
if form.is_valid():
|
|
1237
|
-
user = form.
|
|
1237
|
+
user = form.create()
|
|
1238
1238
|
```
|
|
1239
1239
|
|
|
1240
1240
|
## Architecture
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# plain-postgres changelog
|
|
2
2
|
|
|
3
|
+
## [0.107.0](https://github.com/dropseed/plain/releases/plain-postgres@0.107.0) (2026-06-07)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- **`ModelForm.save()` is replaced by explicit `create()` and `update()`**, mirroring the `Model.create()`/`update()` split from 0.106.0. The view already knows whether it's inserting or updating (`CreateView` vs `UpdateView`), so the form no longer branches on `instance._state.adding` to guess. `form.create()` INSERTs the instance (and its m2m data) and returns it; `form.update(fields=None)` UPDATEs it (and its m2m data) and returns it, with `fields=[...]` passed straight through to `Model.update()` to limit the columns written. `save(commit=...)` — including the `commit=False` deferred path — is removed. ([66634f5af9](https://github.com/dropseed/plain/commit/66634f5af9))
|
|
8
|
+
|
|
9
|
+
### Upgrade instructions
|
|
10
|
+
|
|
11
|
+
- Replace `form.save()` with `form.create()` in create flows and `form.update()` in update flows. The `/plain-upgrade` skill rewrites these for you — the create-vs-update choice is context-dependent, so it reasons per call site.
|
|
12
|
+
- `form.update(fields=[...])` is available to limit the columns written (a pass-through to `Model.update(fields=...)`).
|
|
13
|
+
- 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:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
if form.is_valid():
|
|
17
|
+
form.instance.user = request.user
|
|
18
|
+
obj = form.create()
|
|
19
|
+
```
|
|
20
|
+
|
|
3
21
|
## [0.106.0](https://github.com/dropseed/plain/releases/plain-postgres@0.106.0) (2026-06-03)
|
|
4
22
|
|
|
5
23
|
### What's changed
|
|
@@ -1053,7 +1053,7 @@ except ValidationError:
|
|
|
1053
1053
|
... # report it — the transaction is still usable
|
|
1054
1054
|
```
|
|
1055
1055
|
|
|
1056
|
-
Forms are the exception: a `ModelForm` pre-checks constraints explicitly (a `validate_constraints()` call in its `_post_clean`) so it can surface every violation at once, then
|
|
1056
|
+
Forms are the exception: a `ModelForm` pre-checks constraints explicitly (a `validate_constraints()` call in its `_post_clean`) so it can surface every violation at once, then writes via `form.create()`/`form.update()` with validation already done. A direct `create()`/`update()` reports the first violation Postgres hits.
|
|
1057
1057
|
|
|
1058
1058
|
This applies to instance writes only. Set-based writes — `QuerySet.update()` and `bulk_create()` — raise the raw `psycopg.IntegrityError`, since there's no instance to attribute the error to. If you retry on a unique conflict, catch both:
|
|
1059
1059
|
|
|
@@ -1221,7 +1221,7 @@ class UserForm(forms.ModelForm):
|
|
|
1221
1221
|
# Usage
|
|
1222
1222
|
form = UserForm(request=request)
|
|
1223
1223
|
if form.is_valid():
|
|
1224
|
-
user = form.
|
|
1224
|
+
user = form.create()
|
|
1225
1225
|
```
|
|
1226
1226
|
|
|
1227
1227
|
## Architecture
|
|
@@ -5,6 +5,7 @@ and database field objects.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
from collections.abc import Iterable
|
|
8
9
|
from itertools import chain
|
|
9
10
|
from typing import TYPE_CHECKING, Any, cast
|
|
10
11
|
|
|
@@ -380,30 +381,36 @@ class BaseModelForm(BaseForm):
|
|
|
380
381
|
if f.name in cleaned_data:
|
|
381
382
|
f.save_form_data(self.instance, cleaned_data[f.name])
|
|
382
383
|
|
|
383
|
-
def
|
|
384
|
-
"""
|
|
385
|
-
Save this form's self.instance object if commit=True. Otherwise, add
|
|
386
|
-
a save_m2m() method to the form which can be called after the instance
|
|
387
|
-
is saved manually at a later time. Return the model instance.
|
|
388
|
-
"""
|
|
384
|
+
def _raise_if_invalid(self, action: str) -> None:
|
|
385
|
+
"""Guard the write methods -- a form with errors can't be persisted."""
|
|
389
386
|
if self.errors:
|
|
390
387
|
raise ValueError(
|
|
391
|
-
"The {} could not be
|
|
392
|
-
|
|
393
|
-
"created" if self.instance._state.adding else "changed",
|
|
394
|
-
)
|
|
388
|
+
f"The {self.instance.model_options.object_name} could not be "
|
|
389
|
+
f"{action} because the data didn't validate."
|
|
395
390
|
)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
391
|
+
|
|
392
|
+
def create(self) -> Any:
|
|
393
|
+
"""INSERT this form's instance (and its m2m data) and return it.
|
|
394
|
+
|
|
395
|
+
Shape and constraints were already validated in _post_clean, so the
|
|
396
|
+
write trusts them (clean_and_validate=False). This mirrors
|
|
397
|
+
Model.create(); use it from a create flow and update() to UPDATE.
|
|
398
|
+
"""
|
|
399
|
+
self._raise_if_invalid("created")
|
|
400
|
+
self.instance.create(clean_and_validate=False)
|
|
401
|
+
self._save_m2m()
|
|
402
|
+
return self.instance
|
|
403
|
+
|
|
404
|
+
def update(self, *, fields: Iterable[str] | None = None) -> Any:
|
|
405
|
+
"""UPDATE this form's instance (and its m2m data) and return it.
|
|
406
|
+
|
|
407
|
+
`fields` is passed straight through to Model.update() to limit the
|
|
408
|
+
columns written; the default writes every loaded field. Validation
|
|
409
|
+
already ran in _post_clean, so the write trusts it.
|
|
410
|
+
"""
|
|
411
|
+
self._raise_if_invalid("changed")
|
|
412
|
+
self.instance.update(clean_and_validate=False, fields=fields)
|
|
413
|
+
self._save_m2m()
|
|
407
414
|
return self.instance
|
|
408
415
|
|
|
409
416
|
|
|
@@ -18,6 +18,11 @@ class ExamplesRouter(Router):
|
|
|
18
18
|
views.FormsExampleUpdateView,
|
|
19
19
|
name="forms_update",
|
|
20
20
|
),
|
|
21
|
+
path(
|
|
22
|
+
"forms/<int:pk>/delete",
|
|
23
|
+
views.FormsExampleDeleteView,
|
|
24
|
+
name="forms_delete",
|
|
25
|
+
),
|
|
21
26
|
path(
|
|
22
27
|
"child-cascade/create",
|
|
23
28
|
views.ChildCascadeCreateView,
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from plain.http import Response
|
|
7
|
-
from plain.templates.views import CreateView, UpdateView
|
|
7
|
+
from plain.templates.views import CreateView, DeleteView, UpdateView
|
|
8
8
|
|
|
9
9
|
from .forms import (
|
|
10
10
|
ChildCascadeForm,
|
|
@@ -52,6 +52,13 @@ class FormsExampleUpdateView(_NoTemplateFormView, UpdateView):
|
|
|
52
52
|
return FormsExample.query.filter(id=self.url_kwargs["pk"]).first()
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
class FormsExampleDeleteView(_NoTemplateFormView, DeleteView):
|
|
56
|
+
success_url = "/ok/"
|
|
57
|
+
|
|
58
|
+
def get_object(self) -> Any:
|
|
59
|
+
return FormsExample.query.filter(id=self.url_kwargs["pk"]).first()
|
|
60
|
+
|
|
61
|
+
|
|
55
62
|
class ChildCascadeCreateView(_NoTemplateFormView, CreateView):
|
|
56
63
|
form_class = ChildCascadeForm
|
|
57
64
|
success_url = "/ok/"
|
|
@@ -181,6 +181,34 @@ class TestFormsExampleUpdate:
|
|
|
181
181
|
assert existing.count == 1
|
|
182
182
|
|
|
183
183
|
|
|
184
|
+
class TestFormsExampleDelete:
|
|
185
|
+
"""DeleteView posts an empty confirmation form; the view deletes the row."""
|
|
186
|
+
|
|
187
|
+
def _create_existing(self) -> FormsExample:
|
|
188
|
+
return FormsExample.query.create(
|
|
189
|
+
name="Doomed",
|
|
190
|
+
status="draft",
|
|
191
|
+
count=1,
|
|
192
|
+
ratio=0.5,
|
|
193
|
+
amount=Decimal("1.00"),
|
|
194
|
+
event_date=datetime.date(2020, 1, 1),
|
|
195
|
+
event_time=datetime.time(0, 0, 0),
|
|
196
|
+
event_datetime=datetime.datetime(2020, 1, 1, 0, 0, 0, tzinfo=datetime.UTC),
|
|
197
|
+
duration=datetime.timedelta(minutes=1),
|
|
198
|
+
external_id=uuid.UUID("00000000-0000-0000-0000-000000000002"),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def test_delete_removes_the_row(self, db):
|
|
202
|
+
existing = self._create_existing()
|
|
203
|
+
client = Client()
|
|
204
|
+
|
|
205
|
+
response = client.post(f"/examples/forms/{existing.id}/delete")
|
|
206
|
+
|
|
207
|
+
assert response.status_code == 302, response.content
|
|
208
|
+
assert response.headers["Location"] == "/ok/"
|
|
209
|
+
assert not FormsExample.query.filter(id=existing.id).exists()
|
|
210
|
+
|
|
211
|
+
|
|
184
212
|
class TestForeignKeyRoundTrip:
|
|
185
213
|
"""Exercises the explicit ForeignKeyField → ModelChoiceField handler."""
|
|
186
214
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/related_descriptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/fields/reverse_descriptors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/context.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/helpers.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/ownership.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/runner.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/introspection/health/types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/__init__.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/base.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/fields.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/models.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/plain/postgres/migrations/operations/special.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0006_secretstore.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0010_hideableitem.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/migrations/0016_formsexample.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/relationships.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/app/examples/models/storage_parameters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_autodetector_type_change.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_connection_isolation.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_connection_lifecycle.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_constraint_violation_error.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_constraints.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_defaults.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_indexes.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_nullability.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_convergence_timeouts.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_db_expression_defaults.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_executor_connection_hook.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_fk_characterization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_literal_default_persistence.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_management_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_no_callable_defaults.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_preflight_duplicate_indexes.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_preflight_fk_coverage.py
RENAMED
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/internal/test_schema_normalize_type.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_integrity_error_mapping.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_postgres-0.106.0 → plain_postgres-0.107.0}/tests/public/test_read_only_transactions.py
RENAMED
|
File without changes
|
|
File without changes
|