mirror of
https://github.com/django-polymorphic/django-polymorphic.git
synced 2025-12-06 01:13:55 +03:00
Add tests to all django supported RDBMS to CI, also add macos and windows tests.
Remove development dependency on gettext - compilemessages is run in release action and during just build
This commit is contained in:
parent
282ac7f617
commit
c64b097be2
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
|
@ -64,8 +64,6 @@ jobs:
|
|||
enable-cache: true
|
||||
- name: Install Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install gettext
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
|
|
|
|||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
|
@ -16,10 +16,10 @@ jobs:
|
|||
python-version: "3"
|
||||
cache: pip
|
||||
cache-dependency-path: setup.cfg
|
||||
# TODO: look into using python-babel instead of requiring gettext as system dependency
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext
|
||||
- run: pip install -U build
|
||||
- run: python -m build
|
||||
- run: just build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
|
|
|
|||
732
.github/workflows/test.yml
vendored
732
.github/workflows/test.yml
vendored
|
|
@ -1,50 +1,700 @@
|
|||
name: Test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
- pull_request
|
||||
- push
|
||||
- workflow_dispatch
|
||||
push:
|
||||
tags-ignore:
|
||||
- '*'
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug:
|
||||
description: 'Open ssh debug session.'
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
schedule:
|
||||
- cron: '0 13 * * *' # Runs at 6 am pacific every day
|
||||
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3"
|
||||
cache: pip
|
||||
cache-dependency-path: setup.cfg
|
||||
# TODO: look into using python-babel instead of requiring gettext as system dependency
|
||||
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext
|
||||
- run: pip install -U build
|
||||
- run: python -m build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
Test:
|
||||
|
||||
postgres:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
# Service containers to run with `container-job`
|
||||
strategy:
|
||||
fail-fast: true
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- python-version: "3.9"
|
||||
- python-version: "3.10"
|
||||
- python-version: "3.11"
|
||||
- python-version: "3.12"
|
||||
- python-version: "3.13"
|
||||
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
|
||||
postgres-version: ['9.6', '12', 'latest']
|
||||
psycopg-version: ['psycopg2', 'psycopg3']
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '4.2' # LTS April 2026
|
||||
- '5.1' # December 2025
|
||||
- '5.2' # LTS April 2028
|
||||
exclude:
|
||||
- django-version: '4.2'
|
||||
postgres-version: '9.6'
|
||||
- python-version: '3.11'
|
||||
django-version: '3.2'
|
||||
- python-version: '3.12'
|
||||
django-version: '3.2'
|
||||
- postgres-version: '12'
|
||||
psycopg-version: 'psycopg3'
|
||||
- django-version: '3.2'
|
||||
psycopg-version: 'psycopg3'
|
||||
- django-version: '3.2'
|
||||
postgres-version: 'latest'
|
||||
|
||||
- postgres-version: '12'
|
||||
django-version: '5.1'
|
||||
|
||||
- postgres-version: '9.6'
|
||||
django-version: '5.1'
|
||||
|
||||
- postgres-version: '9.6'
|
||||
django-version: '5.2'
|
||||
|
||||
- postgres-version: '12'
|
||||
django-version: '5.2'
|
||||
|
||||
- python-version: '3.9'
|
||||
django-version: '5.1'
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
- python-version: '3.13'
|
||||
django-version: '3.2'
|
||||
- python-version: '3.13'
|
||||
django-version: '4.2'
|
||||
|
||||
# https://github.com/psycopg/psycopg2/pull/1695
|
||||
- python-version: '3.13'
|
||||
psycopg-version: 'psycopg2'
|
||||
env:
|
||||
RDBMS: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
PGPASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_HOST: localhost
|
||||
POSTGRES_PORT: 5432
|
||||
COVERAGE_FILE: linux-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-${{ matrix.psycopg-version }}-pg${{ matrix.postgres-version }}.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_CLIENT_VERSION: ${{ matrix.psycopg-version }}
|
||||
TEST_DATABASE_VERSION: ${{ matrix.postgres-version }}
|
||||
|
||||
# Service containers to run with `runner-job`
|
||||
services:
|
||||
# Label used to access the service container
|
||||
postgres:
|
||||
# Docker Hub image
|
||||
image: postgres:${{ matrix.postgres-version }}
|
||||
# Provide the password for postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 5432 on service container to the host
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Set up Python ${{ matrix.python-version }}"
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
id: sp
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Create test databases
|
||||
run: |
|
||||
psql -h localhost -p 5432 -U postgres -d postgres -c "CREATE DATABASE test1;"
|
||||
psql -h localhost -p 5432 -U postgres -d postgres -c "CREATE DATABASE test2;"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install Emacs
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
run: |
|
||||
sudo apt install emacs
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just test-lock "Django~=${{ matrix.django-version }}.0"
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
just test-db ${{ matrix.psycopg-version }}
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
|
||||
sqlite:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
env:
|
||||
RDBMS: sqlite
|
||||
COVERAGE_FILE: linux-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-sqlite.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_VERSION: "sqlite"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.9', '3.13']
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '4.2' # LTS April 2026
|
||||
- '5.2' # LTS April 2028
|
||||
exclude:
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
- python-version: '3.13'
|
||||
django-version: '3.2'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
id: sp
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install Emacs
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
run: |
|
||||
sudo apt install emacs
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just install
|
||||
just test-lock Django~=${{ matrix.django-version }}.0
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
just test-db
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
|
||||
mysql:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.9', '3.13']
|
||||
mysql-version: ['5.7', 'latest']
|
||||
mysqlclient-version: ['1.4.3', '']
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '4.2' # LTS April 2026
|
||||
- '5.2' # LTS April 2028
|
||||
exclude:
|
||||
- python-version: '3.13'
|
||||
django-version: '3.2'
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
|
||||
|
||||
- django-version: '3.2'
|
||||
mysql-version: 'latest'
|
||||
- django-version: '4.2'
|
||||
mysql-version: '5.7'
|
||||
- django-version: '5.2'
|
||||
mysql-version: '5.7'
|
||||
|
||||
- mysql-version: '5.7'
|
||||
mysqlclient-version: ''
|
||||
- mysql-version: 'latest'
|
||||
mysqlclient-version: '1.4.3'
|
||||
|
||||
env:
|
||||
RDBMS: mysql
|
||||
MYSQL_VERSION: ${{ matrix.mysql-version }}
|
||||
COVERAGE_FILE: linux-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-${{ matrix.mysqlclient-version }}-mysql${{ matrix.mysql-version }}.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_CLIENT_VERSION: ${{ matrix.mysqlclient-version }}
|
||||
TEST_DATABASE_VERSION: ${{ matrix.mysql-version }}
|
||||
|
||||
services:
|
||||
mysql:
|
||||
# Docker Hub image
|
||||
image: mysql:${{ matrix.mysql-version }}
|
||||
# Provide the password for mysql
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_MULTIPLE_DATABASES: test1,test2
|
||||
# Set health checks to wait until mysql has started
|
||||
options: >-
|
||||
--health-cmd "mysqladmin ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
id: sp
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Emacs
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
run: |
|
||||
sudo apt install emacs
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just test-lock Django~=${{ matrix.django-version }}.0
|
||||
|
||||
- name: Install mysqlclient if needed
|
||||
if: ${{ matrix.mysqlclient-version != '' }}
|
||||
run: just test-lock mysqlclient==${{ matrix.mysqlclient-version }}
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
just test-db mysql
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
mariadb:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
env:
|
||||
RDBMS: mariadb
|
||||
COVERAGE_FILE: linux-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-${{ matrix.mysqlclient-version }}-mariadb${{ matrix.mariadb-version }}.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_CLIENT_VERSION: ${{ matrix.mysqlclient-version }}
|
||||
TEST_DATABASE_VERSION: ${{ matrix.mariadb-version }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.9', '3.13']
|
||||
mysqlclient-version: ['1.4.3', '']
|
||||
mariadb-version: ['10.2', 'latest']
|
||||
mariadb-healthcheck: ["mysqladmin ping", "healthcheck.sh --connect --innodb_initialized"]
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '4.2' # LTS April 2026
|
||||
- '5.2' # LTS April 2028
|
||||
exclude:
|
||||
- python-version: '3.13'
|
||||
django-version: '3.2'
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
|
||||
- django-version: '3.2'
|
||||
mariadb-version: 'latest'
|
||||
- django-version: '4.2'
|
||||
mariadb-version: '10.2'
|
||||
- django-version: '5.2'
|
||||
mariadb-version: '10.2'
|
||||
|
||||
- mariadb-version: '10.2'
|
||||
mysqlclient-version: ''
|
||||
- mariadb-version: 'latest'
|
||||
mysqlclient-version: '1.4.3'
|
||||
|
||||
- mariadb-version: 'latest'
|
||||
mariadb-healthcheck: "mysqladmin ping"
|
||||
- mariadb-version: '10.2'
|
||||
mariadb-healthcheck: "healthcheck.sh --connect --innodb_initialized"
|
||||
|
||||
services:
|
||||
mysql:
|
||||
# Docker Hub image
|
||||
image: mariadb:${{ matrix.mariadb-version }}
|
||||
# Provide the password for mysql
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_MULTIPLE_DATABASES: test1,test2
|
||||
# Set health checks to wait until mysql has started
|
||||
options: >-
|
||||
--health-cmd="${{ matrix.mariadb-healthcheck }}"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
id: sp
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Emacs
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
run: |
|
||||
sudo apt install emacs
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just test-lock Django~=${{ matrix.django-version }}.0
|
||||
|
||||
- name: Install mysqlclient if needed
|
||||
if: ${{ matrix.mysqlclient-version != '' }}
|
||||
run: just test-lock mysqlclient==${{ matrix.mysqlclient-version }}
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
just test-db mysql
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
oracle:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
env:
|
||||
RDBMS: oracle
|
||||
COVERAGE_FILE: linux-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-oracle${{ matrix.oracle-version }}.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_VERSION: ${{ matrix.oracle-version }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ['3.9', '3.10', '3.12']
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '4.2' # LTS April 2026
|
||||
- '5.2' # LTS April 2028
|
||||
oracle-version:
|
||||
- '18'
|
||||
- 'latest'
|
||||
exclude:
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
- python-version: '3.10'
|
||||
django-version: '5.2'
|
||||
- python-version: '3.10'
|
||||
django-version: '3.2'
|
||||
- python-version: '3.12'
|
||||
django-version: '3.2'
|
||||
- python-version: '3.12'
|
||||
django-version: '4.2'
|
||||
- python-version: '3.9'
|
||||
django-version: '4.2'
|
||||
- django-version: '3.2'
|
||||
oracle-version: 'latest'
|
||||
- django-version: '4.2'
|
||||
oracle-version: '18'
|
||||
- django-version: '5.2'
|
||||
oracle-version: '18'
|
||||
|
||||
services:
|
||||
oracle1:
|
||||
image: gvenzl/oracle-xe:${{ matrix.oracle-version }}
|
||||
env:
|
||||
ORACLE_PASSWORD: password
|
||||
ORACLE_DATABASE: test1
|
||||
# Forward Oracle port
|
||||
ports:
|
||||
- 1521:1521
|
||||
# Provide healthcheck script options for startup
|
||||
options: >-
|
||||
--health-cmd healthcheck.sh
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
oracle2:
|
||||
image: gvenzl/oracle-xe:${{ matrix.oracle-version }}
|
||||
env:
|
||||
ORACLE_PASSWORD: password
|
||||
ORACLE_DATABASE: test2
|
||||
# Forward Oracle port
|
||||
ports:
|
||||
- 1522:1521
|
||||
# Provide healthcheck script options for startup
|
||||
options: >-
|
||||
--health-cmd healthcheck.sh
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
id: sp
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "${{ matrix.python-version }}"
|
||||
cache: pip
|
||||
cache-dependency-path: setup.cfg
|
||||
# TODO: look into using python-babel instead of requiring gettext as system dependency
|
||||
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gettext
|
||||
# TODO: look into using python-babel instead of requiring django to be ambiently installed
|
||||
# so tox can build messages
|
||||
- run: pip install -U tox tox-gh-actions django
|
||||
# TODO: postgres setup and proper djangomain testing
|
||||
- run: python -m tox
|
||||
- uses: codecov/codecov-action@v3
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Emacs
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
run: |
|
||||
sudo apt install emacs
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install Oracle Client
|
||||
# https://askubuntu.com/questions/1512196/libaio1-on-noble
|
||||
run: |
|
||||
curl --output oracle-client.rpm https://download.oracle.com/otn_software/linux/instantclient/2116000/oracle-instantclient-basiclite-21.16.0.0.0-1.el8.x86_64.rpm
|
||||
sudo apt install alien libaio1t64
|
||||
sudo alien -i oracle-client.rpm
|
||||
sudo sh -c "echo /usr/lib/oracle/21/client64/lib/ > /etc/ld.so.conf.d/oracle.conf"
|
||||
sudo ln -s /usr/lib/x86_64-linux-gnu/libaio.so.1t64 /usr/lib/x86_64-linux-gnu/libaio.so.1
|
||||
sudo ldconfig
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just test-lock Django~=${{ matrix.django-version }}.0
|
||||
- name: Run Full Unit Tests
|
||||
run: |
|
||||
just test-db oracle
|
||||
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
env:
|
||||
RDBMS: sqlite
|
||||
COVERAGE_FILE: windows-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-sqlite.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_VERSION: "sqlite"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.9', '3.13']
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '5.2' # LTS April 2028
|
||||
exclude:
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
- python-version: '3.13'
|
||||
django-version: '3.2'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
id: sp
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: install-vim-windows
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: rhysd/action-setup-vim@v1
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just install
|
||||
just test-lock Django~=${{ matrix.django-version }}.0
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
just test
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
env:
|
||||
RDBMS: sqlite
|
||||
COVERAGE_FILE: macos-py${{ matrix.python-version }}-dj${{ matrix.django-version }}-sqlite.coverage
|
||||
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
|
||||
TEST_DATABASE_VERSION: "sqlite"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.9', '3.13']
|
||||
django-version:
|
||||
- '3.2' # LTS April 2024
|
||||
- '5.2' # LTS April 2028
|
||||
exclude:
|
||||
- python-version: '3.9'
|
||||
django-version: '5.2'
|
||||
- python-version: '3.13'
|
||||
django-version: '3.2'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
id: sp
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: install-emacs-macos
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
run: |
|
||||
brew install emacs
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event.inputs.debug == 'true' }}
|
||||
uses: mxschmitt/action-tmate@v3.22
|
||||
with:
|
||||
detached: true
|
||||
timeout-minutes: 60
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just install
|
||||
just test-lock Django~=${{ matrix.django-version }}.0
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
just test
|
||||
- name: Store coverage files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.COVERAGE_FILE }}
|
||||
path: ${{ env.COVERAGE_FILE }}
|
||||
|
||||
|
||||
coverage-combine:
|
||||
needs: [postgres, sqlite, mysql, mariadb, oracle, windows, macos]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v5
|
||||
id: sp
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v3
|
||||
- name: Install Release Dependencies
|
||||
run: |
|
||||
just setup ${{ steps.sp.outputs.python-path }}
|
||||
just install
|
||||
|
||||
- name: Get coverage files
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: "*.coverage"
|
||||
merge-multiple: true
|
||||
- run: ls -la *.coverage
|
||||
- run: just coverage
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files:
|
||||
./coverage.xml
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import sys
|
||||
|
||||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
||||
|
||||
|
||||
class CustomBuildHook(BuildHookInterface):
|
||||
"""
|
||||
Hatch build hook to run Django's compilemessages command
|
||||
during the build process. This is necessary to ensure that
|
||||
translation files are compiled and included in the build output.
|
||||
|
||||
See https://hatch.pypa.io/latest/plugins/build-hook/custom/
|
||||
"""
|
||||
|
||||
def initialize(self, version, build_data):
|
||||
from django.core import management
|
||||
|
||||
management.call_command("compilemessages", stdout=sys.stderr, verbosity=1)
|
||||
7
justfile
7
justfile
|
|
@ -89,6 +89,7 @@ build-docs: build-docs-html
|
|||
|
||||
# build docs and package
|
||||
build: build-docs-html
|
||||
@just manage compilemessages
|
||||
uv build
|
||||
|
||||
# open the html documentation
|
||||
|
|
@ -179,6 +180,12 @@ test-lock +PACKAGES: _lock-python
|
|||
test *TESTS:
|
||||
@just run pytest --cov-append {{ TESTS }}
|
||||
|
||||
test-db DB_CLIENT="dev":
|
||||
# No Optional Dependency Unit Tests
|
||||
# todo clean this up, rerunning a lot of tests
|
||||
uv sync --group {{ DB_CLIENT }}
|
||||
@just run pytest --cov-append
|
||||
|
||||
# run the pre-commit checks
|
||||
precommit:
|
||||
@just run pre-commit
|
||||
|
|
|
|||
|
|
@ -78,9 +78,6 @@ exclude = ["src/polymorphic/tests"]
|
|||
packages = ["src/polymorphic"]
|
||||
artifacts = ["*.mo"]
|
||||
|
||||
[tool.hatch.build.targets.wheel.hooks.custom]
|
||||
require-runtime-dependencies = true # We need Django so we have compilemessages
|
||||
|
||||
[tool.doc8]
|
||||
max-line-length = 100
|
||||
sphinx = true
|
||||
|
|
@ -175,5 +172,11 @@ psycopg2 = [
|
|||
"psycopg2>=2.9.10",
|
||||
]
|
||||
psycopg3 = [
|
||||
"psycopg>=3.2.5",
|
||||
"psycopg",
|
||||
]
|
||||
mysql = [
|
||||
"mysqlclient>=1.4.0",
|
||||
]
|
||||
oracle = [
|
||||
"cx-oracle>=8.3.0",
|
||||
]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,101 +13,101 @@ from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTyp
|
|||
|
||||
|
||||
class PlainA(models.Model):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class PlainB(PlainA):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class PlainC(PlainB):
|
||||
field3 = models.CharField(max_length=10)
|
||||
field3 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Model2A(ShowFieldType, PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
polymorphic_showfield_deferred = True
|
||||
|
||||
|
||||
class Model2B(Model2A):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Model2C(Model2B):
|
||||
field3 = models.CharField(max_length=10)
|
||||
field3 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Model2D(Model2C):
|
||||
field4 = models.CharField(max_length=10)
|
||||
field4 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelExtraA(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelExtraB(ModelExtraA):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelExtraC(ModelExtraB):
|
||||
field3 = models.CharField(max_length=10)
|
||||
field3 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelExtraExternal(models.Model):
|
||||
topic = models.CharField(max_length=10)
|
||||
topic = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelShow1(ShowFieldType, PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
m2m = models.ManyToManyField("self")
|
||||
|
||||
|
||||
class ModelShow2(ShowFieldContent, PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
m2m = models.ManyToManyField("self")
|
||||
|
||||
|
||||
class ModelShow3(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
m2m = models.ManyToManyField("self")
|
||||
|
||||
|
||||
class ModelShow1_plain(PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelShow2_plain(ModelShow1_plain):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Base(ShowFieldType, PolymorphicModel):
|
||||
polymorphic_showfield_deferred = True
|
||||
field_b = models.CharField(max_length=10)
|
||||
field_b = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelX(Base):
|
||||
field_x = models.CharField(max_length=10)
|
||||
field_x = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelY(Base):
|
||||
field_y = models.CharField(max_length=10)
|
||||
field_y = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Enhance_Plain(models.Model):
|
||||
field_p = models.CharField(max_length=10)
|
||||
field_p = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Enhance_Base(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
base_id = models.AutoField(primary_key=True)
|
||||
field_b = models.CharField(max_length=10)
|
||||
field_b = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class Enhance_Inherit(Enhance_Base, Enhance_Plain):
|
||||
field_i = models.CharField(max_length=10)
|
||||
field_i = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class RelationBase(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
field_base = models.CharField(max_length=10)
|
||||
field_base = models.CharField(max_length=30)
|
||||
fk = models.ForeignKey(
|
||||
"self", on_delete=models.CASCADE, null=True, related_name="relationbase_set"
|
||||
)
|
||||
|
|
@ -115,15 +115,15 @@ class RelationBase(ShowFieldTypeAndContent, PolymorphicModel):
|
|||
|
||||
|
||||
class RelationA(RelationBase):
|
||||
field_a = models.CharField(max_length=10)
|
||||
field_a = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class RelationB(RelationBase):
|
||||
field_b = models.CharField(max_length=10)
|
||||
field_b = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class RelationBC(RelationB):
|
||||
field_c = models.CharField(max_length=10)
|
||||
field_c = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class RelatingModel(models.Model):
|
||||
|
|
@ -132,23 +132,23 @@ class RelatingModel(models.Model):
|
|||
|
||||
class One2OneRelatingModel(PolymorphicModel):
|
||||
one2one = models.OneToOneField(Model2A, on_delete=models.CASCADE)
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class One2OneRelatingModelDerived(One2OneRelatingModel):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelUnderRelParent(PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
_private = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
_private = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelUnderRelChild(PolymorphicModel):
|
||||
parent = models.ForeignKey(
|
||||
ModelUnderRelParent, on_delete=models.CASCADE, related_name="children"
|
||||
)
|
||||
_private2 = models.CharField(max_length=10)
|
||||
_private2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class MyManagerQuerySet(PolymorphicQuerySet):
|
||||
|
|
@ -169,24 +169,24 @@ class MyManager(PolymorphicManager):
|
|||
|
||||
class ModelWithMyManager(ShowFieldTypeAndContent, Model2A):
|
||||
objects = MyManager()
|
||||
field4 = models.CharField(max_length=10)
|
||||
field4 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelWithMyManagerNoDefault(ShowFieldTypeAndContent, Model2A):
|
||||
objects = PolymorphicManager()
|
||||
my_objects = MyManager()
|
||||
field4 = models.CharField(max_length=10)
|
||||
field4 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelWithMyManagerDefault(ShowFieldTypeAndContent, Model2A):
|
||||
my_objects = MyManager()
|
||||
objects = PolymorphicManager()
|
||||
field4 = models.CharField(max_length=10)
|
||||
field4 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelWithMyManager2(ShowFieldTypeAndContent, Model2A):
|
||||
objects = MyManagerQuerySet.as_manager()
|
||||
field4 = models.CharField(max_length=10)
|
||||
field4 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelArticle(PolymorphicModel):
|
||||
|
|
@ -194,11 +194,11 @@ class ModelArticle(PolymorphicModel):
|
|||
|
||||
|
||||
class ModelPackage(ModelArticle):
|
||||
name = models.CharField(max_length=100)
|
||||
name = models.CharField(max_length=300)
|
||||
|
||||
|
||||
class ModelComponent(ModelArticle):
|
||||
name = models.CharField(max_length=100)
|
||||
name = models.CharField(max_length=300)
|
||||
|
||||
|
||||
class ModelOrderLine(models.Model):
|
||||
|
|
@ -207,7 +207,7 @@ class ModelOrderLine(models.Model):
|
|||
|
||||
class MROBase1(ShowFieldType, PolymorphicModel):
|
||||
objects = MyManager()
|
||||
field1 = models.CharField(max_length=10) # needed as MyManager uses it
|
||||
field1 = models.CharField(max_length=30) # needed as MyManager uses it
|
||||
|
||||
|
||||
class MROBase2(MROBase1):
|
||||
|
|
@ -231,7 +231,7 @@ class ParentModelWithManager(PolymorphicModel):
|
|||
|
||||
class ChildModelWithManager(PolymorphicModel):
|
||||
# Also test whether foreign keys receive the manager:
|
||||
field1 = models.CharField(max_length=10) # needed as MyManager uses it
|
||||
field1 = models.CharField(max_length=30) # needed as MyManager uses it
|
||||
fk = models.ForeignKey(
|
||||
ParentModelWithManager, on_delete=models.CASCADE, related_name="childmodel_set"
|
||||
)
|
||||
|
|
@ -266,11 +266,11 @@ class PlainChildModelWithManager(models.Model):
|
|||
|
||||
|
||||
class BlogBase(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
name = models.CharField(max_length=10)
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class BlogA(BlogBase):
|
||||
info = models.CharField(max_length=10)
|
||||
info = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class BlogB(BlogBase):
|
||||
|
|
@ -279,20 +279,20 @@ class BlogB(BlogBase):
|
|||
|
||||
class BlogEntry(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
blog = models.ForeignKey(BlogA, on_delete=models.CASCADE)
|
||||
text = models.CharField(max_length=10)
|
||||
text = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class BlogEntry_limit_choices_to(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
blog = models.ForeignKey(BlogBase, on_delete=models.CASCADE)
|
||||
text = models.CharField(max_length=10)
|
||||
text = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ModelFieldNameTest(ShowFieldType, PolymorphicModel):
|
||||
modelfieldnametest = models.CharField(max_length=10)
|
||||
modelfieldnametest = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class InitTestModel(ShowFieldType, PolymorphicModel):
|
||||
bar = models.CharField(max_length=100)
|
||||
bar = models.CharField(max_length=300)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["bar"] = self.x()
|
||||
|
|
@ -334,15 +334,15 @@ class UUIDResearchProject(UUIDProject):
|
|||
|
||||
class UUIDPlainA(models.Model):
|
||||
uuid_primary_key = models.UUIDField(primary_key=True, default=uuid.uuid1)
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class UUIDPlainB(UUIDPlainA):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class UUIDPlainC(UUIDPlainB):
|
||||
field3 = models.CharField(max_length=10)
|
||||
field3 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
# base -> proxy
|
||||
|
|
@ -358,14 +358,14 @@ class ProxyChild(ProxyBase):
|
|||
|
||||
|
||||
class NonProxyChild(ProxyBase):
|
||||
name = models.CharField(max_length=10)
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
|
||||
# base -> proxy -> real models
|
||||
|
||||
|
||||
class ProxiedBase(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
name = models.CharField(max_length=10)
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ProxyModelBase(ProxiedBase):
|
||||
|
|
@ -374,16 +374,16 @@ class ProxyModelBase(ProxiedBase):
|
|||
|
||||
|
||||
class ProxyModelA(ProxyModelBase):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class ProxyModelB(ProxyModelBase):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
# test bad field name
|
||||
# class TestBadFieldModel(ShowFieldType, PolymorphicModel):
|
||||
# instance_of = models.CharField(max_length=10)
|
||||
# instance_of = models.CharField(max_length=30)
|
||||
|
||||
|
||||
# validation error: "polymorphic.relatednameclash: Accessor for field 'polymorphic_ctype' clashes
|
||||
|
|
@ -435,18 +435,18 @@ class SwappedModel(AbstractModel):
|
|||
|
||||
|
||||
class InlineParent(models.Model):
|
||||
title = models.CharField(max_length=10)
|
||||
title = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class InlineModelA(PolymorphicModel):
|
||||
parent = models.ForeignKey(
|
||||
InlineParent, related_name="inline_children", on_delete=models.CASCADE
|
||||
)
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class InlineModelB(InlineModelA):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class AbstractProject(PolymorphicModel):
|
||||
|
|
@ -475,30 +475,30 @@ class RubberDuck(Duck):
|
|||
|
||||
|
||||
class MultiTableBase(PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
field1 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class MultiTableDerived(MultiTableBase):
|
||||
field2 = models.CharField(max_length=10)
|
||||
field2 = models.CharField(max_length=30)
|
||||
|
||||
|
||||
class SubclassSelectorAbstractBaseModel(PolymorphicModel):
|
||||
base_field = models.CharField(max_length=10, default="test_bf")
|
||||
base_field = models.CharField(max_length=30, default="test_bf")
|
||||
|
||||
|
||||
class SubclassSelectorAbstractModel(SubclassSelectorAbstractBaseModel):
|
||||
abstract_field = models.CharField(max_length=10, default="test_af")
|
||||
abstract_field = models.CharField(max_length=30, default="test_af")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class SubclassSelectorAbstractConcreteModel(SubclassSelectorAbstractModel):
|
||||
concrete_field = models.CharField(max_length=10, default="test_cf")
|
||||
concrete_field = models.CharField(max_length=30, default="test_cf")
|
||||
|
||||
|
||||
class SubclassSelectorProxyBaseModel(PolymorphicModel):
|
||||
base_field = models.CharField(max_length=10, default="test_bf")
|
||||
base_field = models.CharField(max_length=30, default="test_bf")
|
||||
|
||||
|
||||
class SubclassSelectorProxyModel(SubclassSelectorProxyBaseModel):
|
||||
|
|
@ -507,8 +507,8 @@ class SubclassSelectorProxyModel(SubclassSelectorProxyBaseModel):
|
|||
|
||||
|
||||
class SubclassSelectorProxyConcreteModel(SubclassSelectorProxyModel):
|
||||
concrete_field = models.CharField(max_length=10, default="test_cf")
|
||||
concrete_field = models.CharField(max_length=30, default="test_cf")
|
||||
|
||||
|
||||
class NonPolymorphicParent(PolymorphicModel, Group):
|
||||
test = models.CharField(max_length=22, default="test_non_polymorphic_parent")
|
||||
test = models.CharField(max_length=30, default="test_non_polymorphic_parent")
|
||||
|
|
|
|||
|
|
@ -1,16 +1,93 @@
|
|||
import dj_database_url
|
||||
import os
|
||||
|
||||
DEBUG = False
|
||||
DATABASES = {
|
||||
"default": dj_database_url.config(
|
||||
env="PRIMARY_DATABASE",
|
||||
default="sqlite://:memory:",
|
||||
),
|
||||
"secondary": dj_database_url.config(
|
||||
env="SECONDARY_DATABASE",
|
||||
default="sqlite://:memory:",
|
||||
),
|
||||
}
|
||||
|
||||
rdbms = os.environ.get("RDBMS", "sqlite")
|
||||
|
||||
if rdbms == "sqlite": # pragma: no cover
|
||||
DATABASES = {
|
||||
"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"},
|
||||
"secondary": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"},
|
||||
}
|
||||
elif rdbms == "postgres": # pragma: no cover
|
||||
creds = {
|
||||
"USER": os.environ.get("POSTGRES_USER", "postgres"),
|
||||
"PASSWORD": os.environ.get("POSTGRES_PASSWORD", ""),
|
||||
"HOST": os.environ.get("POSTGRES_HOST", ""),
|
||||
"PORT": os.environ.get("POSTGRES_PORT", ""),
|
||||
}
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "test1",
|
||||
**creds,
|
||||
},
|
||||
"secondary": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "test2",
|
||||
**creds,
|
||||
},
|
||||
}
|
||||
elif rdbms == "mysql": # pragma: no cover
|
||||
dbs = os.environ.get("MYSQL_MULTIPLE_DATABASES", "test1,test2").split(",")
|
||||
creds = {
|
||||
"USER": os.environ.get("MYSQL_USER", "root"),
|
||||
"PASSWORD": os.environ.get("MYSQL_PASSWORD", "root"),
|
||||
"HOST": os.environ.get("MYSQL_HOST", "127.0.0.1"),
|
||||
"PORT": os.environ.get("MYSQL_PORT", "3306"),
|
||||
}
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"NAME": dbs[0],
|
||||
**creds,
|
||||
},
|
||||
"secondary": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"NAME": dbs[1],
|
||||
**creds,
|
||||
},
|
||||
}
|
||||
elif rdbms == "mariadb": # pragma: no cover
|
||||
dbs = os.environ.get("MYSQL_MULTIPLE_DATABASES", "test1,test2").split(",")
|
||||
creds = {
|
||||
"USER": os.environ.get("MYSQL_USER", "root"),
|
||||
"PASSWORD": os.environ.get("MYSQL_ROOT_PASSWORD", "root"),
|
||||
"HOST": os.environ.get("MYSQL_HOST", "127.0.0.1"),
|
||||
"PORT": os.environ.get("MYSQL_PORT", "3306"),
|
||||
}
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"NAME": dbs[0],
|
||||
**creds,
|
||||
},
|
||||
"secondary": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"NAME": dbs[1],
|
||||
**creds,
|
||||
},
|
||||
}
|
||||
elif rdbms == "oracle": # pragma: no cover
|
||||
dbs = os.environ.get("ORACLE_DATABASES", "test1,test2").split(",")
|
||||
ports = os.environ.get("ORACLE_PORTS", "1521,1522").split(",")
|
||||
creds = {
|
||||
"USER": os.environ.get("ORACLE_USER", "system"),
|
||||
"PASSWORD": os.environ.get("ORACLE_PASSWORD", "password"),
|
||||
}
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.oracle",
|
||||
"NAME": f"{os.environ.get('ORACLE_HOST', 'localhost')}:{ports[0]}/{dbs[0]}",
|
||||
**creds,
|
||||
},
|
||||
"secondary": {
|
||||
"ENGINE": "django.db.backends.oracle",
|
||||
"NAME": f"{os.environ.get('ORACLE_HOST', 'localhost')}:{ports[1]}/{dbs[1]}",
|
||||
**creds,
|
||||
},
|
||||
}
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
INSTALLED_APPS = (
|
||||
"django.contrib.auth",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import uuid
|
|||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.db.models import Case, Count, FilteredRelation, Q, Sum, When
|
||||
from django.db.utils import IntegrityError
|
||||
from django.db.models import Case, Count, FilteredRelation, Q, Sum, When, F
|
||||
from django.db.utils import IntegrityError, NotSupportedError
|
||||
from django.test import TransactionTestCase
|
||||
|
||||
from polymorphic import query_translate
|
||||
|
|
@ -138,33 +138,17 @@ class PolymorphicTests(TransactionTestCase):
|
|||
<BlogA: id 1, name (CharField) "B1", info (CharField) "i1"> ]"""
|
||||
assert repr(BlogBase.objects.order_by("-name")).strip() == expected.strip()
|
||||
|
||||
# test ordering for field in one subclass only
|
||||
# MySQL and SQLite return this order
|
||||
expected1 = """
|
||||
[ <BlogA: id 8, name (CharField) "B5", info (CharField) "i5">,
|
||||
<BlogA: id 7, name (CharField) "B4", info (CharField) "i4">,
|
||||
<BlogA: id 6, name (CharField) "B3", info (CharField) "i3">,
|
||||
<BlogA: id 5, name (CharField) "B2", info (CharField) "i2">,
|
||||
<BlogA: id 1, name (CharField) "B1", info (CharField) "i1">,
|
||||
<BlogB: id 2, name (CharField) "Bb1">,
|
||||
<BlogB: id 3, name (CharField) "Bb2">,
|
||||
<BlogB: id 4, name (CharField) "Bb3"> ]"""
|
||||
|
||||
# PostgreSQL returns this order
|
||||
expected2 = """
|
||||
[ <BlogB: id 2, name (CharField) "Bb1">,
|
||||
<BlogB: id 3, name (CharField) "Bb2">,
|
||||
<BlogB: id 4, name (CharField) "Bb3">,
|
||||
<BlogA: id 8, name (CharField) "B5", info (CharField) "i5">,
|
||||
<BlogA: id 7, name (CharField) "B4", info (CharField) "i4">,
|
||||
<BlogA: id 6, name (CharField) "B3", info (CharField) "i3">,
|
||||
<BlogA: id 5, name (CharField) "B2", info (CharField) "i2">,
|
||||
<BlogA: id 1, name (CharField) "B1", info (CharField) "i1"> ]"""
|
||||
|
||||
assert repr(BlogBase.objects.order_by("-BlogA___info")).strip() in (
|
||||
expected1.strip(),
|
||||
expected2.strip(),
|
||||
)
|
||||
# different RDBMS return different orders for the nulls, and we can't use F
|
||||
# and nulls_first or nulls_last here to standardize it, so our test is
|
||||
# conditional
|
||||
blog_names = [blg.name for blg in BlogBase.objects.order_by("-BlogA___info")]
|
||||
ordered = blog_names[:3]
|
||||
if all([name.startswith("Bb") for name in ordered]):
|
||||
ordered = blog_names[3:]
|
||||
else:
|
||||
assert all([name.startswith("Bb") for name in blog_names[-3:]])
|
||||
ordered = blog_names[:-3]
|
||||
assert ordered == ["B5", "B4", "B3", "B2", "B1"]
|
||||
|
||||
def test_limit_choices_to(self):
|
||||
"""
|
||||
|
|
@ -524,6 +508,8 @@ class PolymorphicTests(TransactionTestCase):
|
|||
)
|
||||
|
||||
def test_extra_method(self):
|
||||
from django.db import connection
|
||||
|
||||
a, b, c, d = self.create_model2abcd()
|
||||
|
||||
objects = Model2A.objects.extra(where=[f"id IN ({b.id}, {c.id})"])
|
||||
|
|
@ -531,11 +517,18 @@ class PolymorphicTests(TransactionTestCase):
|
|||
objects, [Model2B, Model2C], transform=lambda o: o.__class__, ordered=False
|
||||
)
|
||||
|
||||
objects = Model2A.objects.extra(
|
||||
select={"select_test": "field1 = 'A1'"},
|
||||
where=["field1 = 'A1' OR field1 = 'B1'"],
|
||||
order_by=["-id"],
|
||||
)
|
||||
if connection.vendor == "oracle":
|
||||
objects = Model2A.objects.extra(
|
||||
select={"select_test": "CASE WHEN field1 = 'A1' THEN 1 ELSE 0 END"},
|
||||
where=["field1 = 'A1' OR field1 = 'B1'"],
|
||||
order_by=["-id"],
|
||||
)
|
||||
else:
|
||||
objects = Model2A.objects.extra(
|
||||
select={"select_test": "field1 = 'A1'"},
|
||||
where=["field1 = 'A1' OR field1 = 'B1'"],
|
||||
order_by=["-id"],
|
||||
)
|
||||
self.assertQuerySetEqual(objects, [Model2B, Model2A], transform=lambda o: o.__class__)
|
||||
|
||||
ModelExtraA.objects.create(field1="A1")
|
||||
|
|
@ -723,7 +716,7 @@ class PolymorphicTests(TransactionTestCase):
|
|||
== '<RelationBase: id 1, field_base (CharField) "base", fk (ForeignKey) None, m2m (ManyToManyField) 0>'
|
||||
)
|
||||
|
||||
objects = oa.relationbase_set.all()
|
||||
objects = oa.relationbase_set.order_by("pk").all()
|
||||
assert (
|
||||
repr(objects[0])
|
||||
== '<RelationB: id 3, field_base (CharField) "B1", fk (ForeignKey) RelationA, field_b (CharField) "B2", m2m (ManyToManyField) 1>'
|
||||
|
|
@ -741,7 +734,7 @@ class PolymorphicTests(TransactionTestCase):
|
|||
)
|
||||
|
||||
oa = RelationA.objects.get()
|
||||
objects = oa.m2m.all()
|
||||
objects = oa.m2m.order_by("pk").all()
|
||||
assert (
|
||||
repr(objects[0])
|
||||
== '<RelationA: id 2, field_base (CharField) "A1", fk (ForeignKey) RelationBase, field_a (CharField) "A2", m2m (ManyToManyField) 2>'
|
||||
|
|
@ -1151,14 +1144,21 @@ class PolymorphicTests(TransactionTestCase):
|
|||
)
|
||||
|
||||
def test_bulk_create_ignore_conflicts(self):
|
||||
ArtProject.objects.bulk_create(
|
||||
[
|
||||
ArtProject(topic="Painting with Tim", artist="T. Turner"),
|
||||
ArtProject.objects.create(topic="Sculpture with Tim", artist="T. Turner"),
|
||||
],
|
||||
ignore_conflicts=True,
|
||||
)
|
||||
assert ArtProject.objects.count() == 2
|
||||
try:
|
||||
ArtProject.objects.bulk_create(
|
||||
[
|
||||
ArtProject(topic="Painting with Tim", artist="T. Turner"),
|
||||
ArtProject.objects.create(topic="Sculpture with Tim", artist="T. Turner"),
|
||||
],
|
||||
ignore_conflicts=True,
|
||||
)
|
||||
assert ArtProject.objects.count() == 2
|
||||
except NotSupportedError:
|
||||
from django.db import connection
|
||||
|
||||
assert connection.vendor in ("oracle"), (
|
||||
f"{connection.vendor} should support ignore_conflicts"
|
||||
)
|
||||
|
||||
def test_bulk_create_no_ignore_conflicts(self):
|
||||
with pytest.raises(IntegrityError):
|
||||
|
|
|
|||
39
uv.lock
39
uv.lock
|
|
@ -448,6 +448,20 @@ toml = [
|
|||
{ name = "tomli", marker = "python_full_version <= '3.11'" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cx-oracle"
|
||||
version = "8.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e8/16/13c265afc984796fe38ee928733569b599cfd657245ddd1afad238b66656/cx_Oracle-8.3.0.tar.gz", hash = "sha256:3b2d215af4441463c97ea469b9cc307460739f89fdfa8ea222ea3518f1a424d9", size = 363886, upload-time = "2021-11-04T22:08:34.141Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/b7/c2d0223fb4f1013b090cf82f3ce56f36f33b79a48f9c33b36717c2977b04/cx_Oracle-8.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b6a23da225f03f50a81980c61dbd6a358c3575f212ca7f4c22bb65a9faf94f7f", size = 892553, upload-time = "2021-11-04T22:08:43.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/5b/8294aa9db4b54a3be10bec66c812d2527ca02a3290b1475c0e5faa1e70bc/cx_Oracle-8.3.0-cp310-cp310-win32.whl", hash = "sha256:715a8bbda5982af484ded14d184304cc552c1096c82471dd2948298470e88a04", size = 147835, upload-time = "2021-11-04T22:08:44.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/a3/b671dee7f34971acf553df0c28818da6bf53b1a64901303f904c12ff443a/cx_Oracle-8.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:07f01608dfb6603a8f2a868fc7c7bdc951480f187df8dbc50f4d48c884874e6a", size = 213059, upload-time = "2021-11-04T22:08:46.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/23/d495b40eb7950e8c4d3a8f22a92552b5d173b92479c60ad7f9355ad51666/cx_Oracle-8.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:df412238a9948340591beee9ec64fa62a2efacc0d91107034a7023e2991fba97", size = 888235, upload-time = "2021-11-04T22:09:02.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/a5/3f0c75239755194635a4ab4e4a2241413d66d2b70672ee6d9c9dede2d1da/cx_Oracle-8.3.0-cp39-cp39-win32.whl", hash = "sha256:70d3cf030aefd71f99b45beba77237b2af448adf5e26be0db3d0d3dee6ea4230", size = 148181, upload-time = "2021-11-04T22:09:03.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/f7/bc903192bed4c2738735753a796b894815a78472648d0983beca68b91196/cx_Oracle-8.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf01ce87edb4ef663b2e5bd604e1e0154d2cc2f12b60301f788b569d9db8a900", size = 213158, upload-time = "2021-11-04T22:09:04.718Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.2.1"
|
||||
|
|
@ -565,6 +579,12 @@ docs = [
|
|||
{ name = "sphinx-rtd-theme" },
|
||||
{ name = "sphinxcontrib-django" },
|
||||
]
|
||||
mysql = [
|
||||
{ name = "mysqlclient" },
|
||||
]
|
||||
oracle = [
|
||||
{ name = "cx-oracle" },
|
||||
]
|
||||
psycopg2 = [
|
||||
{ name = "psycopg2" },
|
||||
]
|
||||
|
|
@ -600,8 +620,10 @@ docs = [
|
|||
{ name = "sphinx-rtd-theme", specifier = ">=3.0.2" },
|
||||
{ name = "sphinxcontrib-django", specifier = ">=2.5" },
|
||||
]
|
||||
mysql = [{ name = "mysqlclient", specifier = ">=1.4.0" }]
|
||||
oracle = [{ name = "cx-oracle", specifier = ">=8.3.0" }]
|
||||
psycopg2 = [{ name = "psycopg2", specifier = ">=2.9.10" }]
|
||||
psycopg3 = [{ name = "psycopg", specifier = ">=3.2.5" }]
|
||||
psycopg3 = [{ name = "psycopg" }]
|
||||
|
||||
[[package]]
|
||||
name = "doc8"
|
||||
|
|
@ -1005,6 +1027,21 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mysqlclient"
|
||||
version = "2.2.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/61/68/810093cb579daae426794bbd9d88aa830fae296e85172d18cb0f0e5dd4bc/mysqlclient-2.2.7.tar.gz", hash = "sha256:24ae22b59416d5fcce7e99c9d37548350b4565baac82f95e149cac6ce4163845", size = 91383, upload-time = "2025-01-10T12:06:00.763Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/24/cdaaef42aac7d53c0a01bb638da64961c293b1b6d204efd47400a68029d4/mysqlclient-2.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:2e3c11f7625029d7276ca506f8960a7fd3c5a0a0122c9e7404e6a8fe961b3d22", size = 207748, upload-time = "2025-01-10T11:56:24.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/e3/3e2de3f93cd60dd63bd229ec3e3b679f682982614bf513d046c2722aa4ce/mysqlclient-2.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:a22d99d26baf4af68ebef430e3131bb5a9b722b79a9fcfac6d9bbf8a88800687", size = 207745, upload-time = "2025-01-10T11:56:28.67Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/b5/2a8a4bcba3440550f358b839638fe8ec9146fa3c9194890b4998a530c926/mysqlclient-2.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:4b4c0200890837fc64014cc938ef2273252ab544c1b12a6c1d674c23943f3f2e", size = 208032, upload-time = "2025-01-10T11:56:29.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/01/e80141f1cd0459e4c9a5dd309dee135bbae41d6c6c121252fdd853001a8a/mysqlclient-2.2.7-cp313-cp313-win_amd64.whl", hash = "sha256:201a6faa301011dd07bca6b651fe5aaa546d7c9a5426835a06c3172e1056a3c5", size = 208000, upload-time = "2025-01-10T11:56:32.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/e0/524b0777524e0d410f71987f556dd6a0e3273fdb06cd6e91e96afade7220/mysqlclient-2.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:199dab53a224357dd0cb4d78ca0e54018f9cee9bf9ec68d72db50e0a23569076", size = 207857, upload-time = "2025-01-10T11:56:33.666Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/cc/5b1570be9f8597ee41e2a0bd7b62ba861ec2c81898d9449f3d6bfbe15d29/mysqlclient-2.2.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:92af368ed9c9144737af569c86d3b6c74a012a6f6b792eb868384787b52bb585", size = 207800, upload-time = "2025-01-10T11:56:36.023Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/40/b5d03494c1caa8f4da171db41d8d9d5b0d8959f893761597d97420083362/mysqlclient-2.2.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:977e35244fe6ef44124e9a1c2d1554728a7b76695598e4b92b37dc2130503069", size = 207965, upload-time = "2025-01-10T11:56:37.252Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nh3"
|
||||
version = "0.3.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user