├── .bumpversion.cfg
├── .coveragerc
├── .editorconfig
├── .fussyfox.yml
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ └── feature_request.yaml
├── dependabot.yml
└── workflows
│ ├── auto-approve.yml
│ ├── auto-merge.yml
│ ├── codacy-analysis.yml
│ ├── codeql.yml
│ ├── deploy.yml
│ ├── gh-pages.yml
│ ├── greetings.yml
│ ├── test.yml
│ └── update-doc-assets.yml
├── .gitignore
├── .pep8speaks.yml
├── .pre-commit-config.yaml
├── .pypirc
├── .pyup.yml
├── .whitesource
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE-APACHE
├── LICENSE-MIT
├── MANIFEST.in
├── Makefile
├── README.md
├── demo
├── __init__.py
├── apps.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
└── models.py
├── django_extra_field_validation
├── __init__.py
├── settings.py
└── wsgi.py
├── docs
├── .nojekyll
├── README.md
└── index.html
├── extra_validator
├── __init__.py
├── apps.py
├── field_validation
│ ├── __init__.py
│ └── validator.py
└── tests.py
├── manage.py
├── pyproject.toml
├── pytest.ini
├── renovate.json
├── requirements.txt
├── setup.py
└── tox.ini
/.bumpversion.cfg:
--------------------------------------------------------------------------------
1 | [bumpversion]
2 | current_version = 1.2.3
3 | commit = True
4 | tag = False
5 |
6 | [bumpversion:file:setup.py]
7 | search = version="{current_version}"
8 | replace = version="{new_version}"
9 |
10 | [bumpversion:file:extra_validator/__init__.py]
11 | search = __version__ = "{current_version}"
12 | replace = __version__ = "{new_version}"
13 |
14 | [bdist_wheel]
15 | universal = 1
16 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | source = extra_validator
3 | omit =
4 | .tox
5 | demo/apps.py
6 | django_extra_field_validation/wsgi.py
7 | setup.py
8 | manage.py
9 | extra_validator/apps.py
10 | extra_validator/tests.py
11 | branch = True
12 |
13 | [report]
14 | exclude_lines =
15 | no cov
16 | no qa
17 | noqa
18 | pragma: no cover
19 | if __name__ == .__main__.:
20 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.{py,rst,ini}]
12 | indent_style = space
13 | indent_size = 4
14 |
15 | [*.{html,css,scss,json,yml}]
16 | indent_style = space
17 | indent_size = 2
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 |
22 | [Makefile]
23 | indent_style = tab
24 |
--------------------------------------------------------------------------------
/.fussyfox.yml:
--------------------------------------------------------------------------------
1 | ## list of checks you would like to run on this repository
2 | # e.g.:
3 | - flake8
4 | - black
5 | # - pycodestyle
6 | # - pydocstyle
7 | # - pyflakes
8 | # - bandit
9 | # - isort
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: jackton1
4 | patreon: # Replace with a single Patreon username
5 | open_collective: tj-django
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: []
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yaml:
--------------------------------------------------------------------------------
1 | name: 🐞 Bug
2 | description: Create a report to help us improve
3 | title: "[BUG]
"
4 | labels: [bug, needs triage]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Thanks for taking the time to fill out this bug report!
11 | - type: checkboxes
12 | attributes:
13 | label: Is there an existing issue for this?
14 | description: Please search to see if an issue already exists for the bug you encountered.
15 | options:
16 | - label: I have searched the existing issues
17 | required: true
18 | - type: checkboxes
19 | attributes:
20 | label: Does this issue exist in the latest version?
21 | description: Please view all releases to confirm that this issue hasn't already been fixed.
22 | options:
23 | - label: I'm using the latest release
24 | required: true
25 | - type: textarea
26 | id: what-happened
27 | attributes:
28 | label: Describe the bug?
29 | description: A clear and concise description of what the bug is
30 | placeholder: Tell us what you see!
31 | validations:
32 | required: true
33 | - type: textarea
34 | id: reproduce
35 | attributes:
36 | label: To Reproduce
37 | description: Steps to reproduce the behavior?
38 | placeholder: |
39 | 1. In this environment...
40 | 2. With this config...
41 | 3. Run '...'
42 | 4. See error...
43 | validations:
44 | required: true
45 | - type: dropdown
46 | id: os
47 | attributes:
48 | label: What OS are you seeing the problem on?
49 | multiple: true
50 | options:
51 | - Ubuntu
52 | - macOS
53 | - Windows
54 | - Other
55 | validations:
56 | required: false
57 | - type: textarea
58 | id: expected
59 | attributes:
60 | label: Expected behavior?
61 | description: A clear and concise description of what you expected to happen.
62 | placeholder: Tell us what you expected!
63 | validations:
64 | required: true
65 | - type: textarea
66 | id: logs
67 | attributes:
68 | label: Relevant log output
69 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
70 | placeholder: |
71 | This can be achieved by:
72 | 1. Re-running the workflow with debug logging enabled.
73 | 2. Copy or download the log archive.
74 | 3. Paste the contents here or upload the file in a subsequent comment.
75 | render: shell
76 | - type: textarea
77 | attributes:
78 | label: Anything else?
79 | description: |
80 | Links? or References?
81 |
82 | Anything that will give us more context about the issue you are encountering!
83 |
84 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
85 | validations:
86 | required: false
87 | - type: checkboxes
88 | id: terms
89 | attributes:
90 | label: Code of Conduct
91 | description: By submitting this issue, you agree to follow our [Code of Conduct](../blob/main/CODE_OF_CONDUCT.md)
92 | options:
93 | - label: I agree to follow this project's Code of Conduct
94 | required: true
95 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yaml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: Suggest an idea for this project
3 | title: "[Feature] "
4 | labels: [enhancement]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Thanks for taking the time to fill out this feature request!
11 | - type: checkboxes
12 | attributes:
13 | label: Is this feature missing in the latest version?
14 | description: Please upgrade to the latest version to verify that this feature is still missing.
15 | options:
16 | - label: I'm using the latest release
17 | required: true
18 | - type: textarea
19 | id: what-happened
20 | attributes:
21 | label: Is your feature request related to a problem? Please describe.
22 | description: |
23 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
24 | placeholder: Tell us what you see!
25 | validations:
26 | required: true
27 | - type: textarea
28 | id: requests
29 | attributes:
30 | label: Describe the solution you'd like?
31 | description: A clear and concise description of what you want to happen.
32 | validations:
33 | required: true
34 | - type: textarea
35 | id: alternative
36 | attributes:
37 | label: Describe alternatives you've considered?
38 | description: A clear and concise description of any alternative solutions or features you've considered.
39 | validations:
40 | required: false
41 | - type: textarea
42 | attributes:
43 | label: Anything else?
44 | description: |
45 | Links? or References?
46 |
47 | Add any other context or screenshots about the feature request here.
48 |
49 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
50 | validations:
51 | required: false
52 | - type: checkboxes
53 | id: terms
54 | attributes:
55 | label: Code of Conduct
56 | description: By submitting this issue, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md)
57 | options:
58 | - label: I agree to follow this project's Code of Conduct
59 | required: true
60 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: pip
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | labels:
9 | - "dependencies"
10 | - "dependabot"
11 | - "automerge"
12 | - package-ecosystem: github-actions
13 | directory: "/"
14 | schedule:
15 | interval: daily
16 | open-pull-requests-limit: 10
17 | labels:
18 | - "dependencies"
19 | - "dependabot"
20 | - "automerge"
21 |
--------------------------------------------------------------------------------
/.github/workflows/auto-approve.yml:
--------------------------------------------------------------------------------
1 | name: Auto approve
2 |
3 | on:
4 | pull_request_target
5 |
6 | jobs:
7 | auto-approve:
8 | runs-on: ubuntu-latest
9 | if: |
10 | (
11 | github.event.pull_request.user.login == 'dependabot[bot]' ||
12 | github.event.pull_request.user.login == 'dependabot' ||
13 | github.event.pull_request.user.login == 'dependabot-preview[bot]' ||
14 | github.event.pull_request.user.login == 'dependabot-preview' ||
15 | github.event.pull_request.user.login == 'renovate[bot]' ||
16 | github.event.pull_request.user.login == 'renovate' ||
17 | github.event.pull_request.user.login == 'github-actions[bot]' ||
18 | github.event.pull_request.user.login == 'pre-commit-ci' ||
19 | github.event.pull_request.user.login == 'pre-commit-ci[bot]'
20 | )
21 | &&
22 | (
23 | github.actor == 'dependabot[bot]' ||
24 | github.actor == 'dependabot' ||
25 | github.actor == 'dependabot-preview[bot]' ||
26 | github.actor == 'dependabot-preview' ||
27 | github.actor == 'renovate[bot]' ||
28 | github.actor == 'renovate' ||
29 | github.actor == 'github-actions[bot]' ||
30 | github.actor == 'pre-commit-ci' ||
31 | github.actor == 'pre-commit-ci[bot]'
32 | )
33 | steps:
34 | - uses: hmarr/auto-approve-action@v4
35 | with:
36 | github-token: ${{ secrets.PAT_TOKEN }}
37 |
--------------------------------------------------------------------------------
/.github/workflows/auto-merge.yml:
--------------------------------------------------------------------------------
1 | name: automerge
2 | on:
3 | check_suite:
4 | types:
5 | - completed
6 |
7 | jobs:
8 | automerge:
9 | runs-on: ubuntu-latest
10 | if: |
11 | github.actor == 'dependabot[bot]' ||
12 | github.actor == 'dependabot' ||
13 | github.actor == 'dependabot-preview[bot]' ||
14 | github.actor == 'dependabot-preview' ||
15 | github.actor == 'renovate[bot]' ||
16 | github.actor == 'renovate'
17 | steps:
18 | - name: automerge
19 | uses: pascalgn/automerge-action@v0.16.4
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
22 | MERGE_METHOD: "rebase"
23 | UPDATE_METHOD: "rebase"
24 | MERGE_RETRIES: "6"
25 | MERGE_RETRY_SLEEP: "100000"
26 | MERGE_LABELS: ""
27 |
--------------------------------------------------------------------------------
/.github/workflows/codacy-analysis.yml:
--------------------------------------------------------------------------------
1 | # This workflow checks out code, performs a Codacy security scan
2 | # and integrates the results with the
3 | # GitHub Advanced Security code scanning feature. For more information on
4 | # the Codacy security scan action usage and parameters, see
5 | # https://github.com/codacy/codacy-analysis-cli-action.
6 | # For more information on Codacy Analysis CLI in general, see
7 | # https://github.com/codacy/codacy-analysis-cli.
8 |
9 | name: Codacy Security Scan
10 |
11 | on:
12 | push:
13 | branches: [ main ]
14 | pull_request:
15 | # The branches below must be a subset of the branches above
16 | branches: [ main ]
17 | schedule:
18 | - cron: '15 16 * * 2'
19 |
20 | jobs:
21 | codacy-security-scan:
22 | name: Codacy Security Scan
23 | runs-on: ubuntu-latest
24 | steps:
25 | # Checkout the repository to the GitHub Actions runner
26 | - name: Checkout code
27 | uses: actions/checkout@v4
28 |
29 | # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
30 | - name: Run Codacy Analysis CLI
31 | uses: codacy/codacy-analysis-cli-action@v4.4.5
32 | with:
33 | # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
34 | # You can also omit the token and run the tools that support default configurations
35 | project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
36 | verbose: true
37 | output: results.sarif
38 | format: sarif
39 | # Adjust severity of non-security issues
40 | gh-code-scanning-compat: true
41 | # Force 0 exit code to allow SARIF file generation
42 | # This will handover control about PR rejection to the GitHub side
43 | max-allowed-issues: 2147483647
44 |
45 | # Upload the SARIF file generated in the previous step
46 | - name: Upload SARIF results file
47 | uses: github/codeql-action/upload-sarif@v3
48 | with:
49 | sarif_file: results.sarif
50 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 | schedule:
9 | - cron: "47 14 * * 1"
10 |
11 | jobs:
12 | analyze:
13 | name: Analyze
14 | runs-on: ubuntu-latest
15 | permissions:
16 | actions: read
17 | contents: read
18 | security-events: write
19 |
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | language: [ python ]
24 |
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v4
28 |
29 | - name: Initialize CodeQL
30 | uses: github/codeql-action/init@v3
31 | with:
32 | languages: ${{ matrix.language }}
33 | queries: +security-and-quality
34 |
35 | - name: Autobuild
36 | uses: github/codeql-action/autobuild@v3
37 |
38 | - name: Perform CodeQL Analysis
39 | uses: github/codeql-action/analyze@v3
40 | with:
41 | category: "/language:${{ matrix.language }}"
42 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Upload Python Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | with:
13 | fetch-depth: 0
14 |
15 | - name: Run semver-diff
16 | id: semver-diff
17 | uses: tj-actions/semver-diff@v3.0.1
18 |
19 | - name: Set up Python
20 | uses: actions/setup-python@v5
21 | with:
22 | python-version: '3.7.x'
23 |
24 | - name: Upgrade pip
25 | run: |
26 | pip install -U pip
27 |
28 | - name: Install dependencies
29 | run: make install-deploy
30 |
31 | - name: Setup git
32 | run: |
33 | git config --local user.email "github-actions[bot]@users.noreply.github.com"
34 | git config --local user.name "github-actions[bot]"
35 |
36 | - name: bumpversion
37 | run: |
38 | make increase-version PART="${{ steps.semver-diff.outputs.release_type }}"
39 |
40 | - name: Build and publish
41 | run: make release
42 | env:
43 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
44 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
45 |
46 | - name: Generate CHANGELOG
47 | uses: tj-actions/github-changelog-generator@v1.20
48 |
49 | - name: Create Pull Request
50 | uses: peter-evans/create-pull-request@v7
51 | with:
52 | base: "main"
53 | title: "Upgraded ${{ steps.semver-diff.outputs.old_version }} → ${{ steps.semver-diff.outputs.new_version }}"
54 | branch: "chore/upgrade-${{ steps.semver-diff.outputs.old_version }}-to-${{ steps.semver-diff.outputs.new_version }}"
55 | commit-message: "Upgraded from ${{ steps.semver-diff.outputs.old_version }} → ${{ steps.semver-diff.outputs.new_version }}"
56 | body: "View [CHANGES](https://github.com/${{ github.repository }}/compare/${{ steps.semver-diff.outputs.old_version }}...${{ steps.semver-diff.outputs.new_version }})"
57 | token: ${{ secrets.PAT_TOKEN }}
58 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: Github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4.2.2
13 | with:
14 | fetch-depth: 0
15 |
16 | - name: Deploy
17 | uses: peaceiris/actions-gh-pages@v4.0.0
18 | with:
19 | github_token: ${{ secrets.GITHUB_TOKEN }}
20 | publish_dir: ./docs
21 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | name: Greetings
2 |
3 | on: [pull_request_target, issues]
4 |
5 | jobs:
6 | greeting:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/first-interaction@v1
10 | with:
11 | repo-token: ${{ secrets.GITHUB_TOKEN }}
12 | issue-message: "Thanks for reporting this issue, don't forget to star this project if you haven't already to help us reach a wider audience."
13 | pr-message: "Thanks for implementing a fix, could you ensure that the test covers your changes if applicable."
14 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI Test
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | paths-ignore:
7 | - "README.md"
8 | - "docs/**"
9 | pull_request:
10 | branches: [ main ]
11 | paths-ignore:
12 | - "README.md"
13 | - "docs/**"
14 |
15 | jobs:
16 | build:
17 | runs-on: ${{ matrix.platform }}
18 | strategy:
19 | matrix:
20 | platform: [ubuntu-20.04, ubuntu-latest, macos-latest, windows-latest]
21 | python-version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11']
22 | exclude:
23 | - platform: ubuntu-latest
24 | python-version: 3.6
25 | - platform: macos-latest
26 | python-version: '3.11'
27 | - platform: windows-latest
28 | python-version: 3.6
29 | - platform: windows-latest
30 | python-version: '3.11'
31 |
32 | steps:
33 | - uses: actions/checkout@v4.2.2
34 | - name: Set up Python ${{ matrix.python-version }}
35 | uses: actions/setup-python@v5.4.0
36 | with:
37 | python-version: ${{ matrix.python-version }}
38 | cache: 'pip'
39 | cache-dependency-path: '**/requirements.txt'
40 | - name: Install dependencies
41 | run: |
42 | python -m pip install --upgrade pip
43 | pip install tox tox-gh-actions
44 | - name: Test with tox
45 | run: tox
46 | env:
47 | DJANGO_SETTINGS_MODULE: django_extra_field_validation.settings
48 | CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
49 | PLATFORM: ${{ matrix.platform }}
50 |
--------------------------------------------------------------------------------
/.github/workflows/update-doc-assets.yml:
--------------------------------------------------------------------------------
1 | name: Sync doc assets.
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | sync-readme:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 |
16 | - name: Run remark
17 | uses: tj-actions/remark@v3
18 |
19 | - name: Verify Changed files
20 | uses: tj-actions/verify-changed-files@v20
21 | id: verify_changed_files
22 | with:
23 | files: |
24 | README.md
25 |
26 | - name: README.md changed
27 | if: steps.verify_changed_files.outputs.files_changed == 'true'
28 | run: |
29 | echo "README.md has uncommited changes"
30 | exit 1
31 |
32 | - name: Create Pull Request
33 | if: failure()
34 | uses: peter-evans/create-pull-request@v7
35 | with:
36 | base: "main"
37 | title: "Updated README.md"
38 | branch: "chore/update-readme"
39 | commit-message: "Updated README.md"
40 | body: "Updated README.md"
41 | token: ${{ secrets.PAT_TOKEN }}
42 |
43 | - name: Copy README
44 | run: |
45 | cp -f README.md docs/README.md
46 |
47 | - name: Create Pull Request
48 | uses: peter-evans/create-pull-request@v7.0.6
49 | with:
50 | commit-message: Synced README changes to docs
51 | committer: github-actions[bot]
52 | author: github-actions[bot]
53 | branch: chore/update-docs
54 | base: main
55 | delete-branch: true
56 | title: Updated docs
57 | body: |
58 | Updated docs
59 | - Auto-generated by github-actions[bot]
60 | assignees: jackton1
61 | reviewers: jackton1
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | *.pyc
3 | .cache/
4 | .coverage
5 | .idea/
6 | .vscode/
7 | *.egg-info/
8 | build/
9 | dist/
10 | docs/build/
11 | venv/
12 | wheelhouse/
13 | .tox
14 | *.sqlite3
15 | .DS_*
16 | pip-wheel-metadata/
17 | .envrc
18 | coverage.xml
19 |
--------------------------------------------------------------------------------
/.pep8speaks.yml:
--------------------------------------------------------------------------------
1 | scanner:
2 | diff_only: True # If False, the entire file touched by the Pull Request is scanned for errors. If True, only the diff is scanned.
3 | linter: pycodestyle
4 |
5 | pycodestyle: # Same as scanner.linter value. Other option is flake8
6 | max-line-length: 100 # Default is 79 in PEP 8
7 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/PyCQA/autoflake
3 | rev: v2.1.1
4 | hooks:
5 | - id: autoflake
6 | args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variable']
7 |
8 | - repo: https://github.com/pycqa/isort
9 | rev: 5.12.0
10 | hooks:
11 | - id: isort
12 | args: ["--profile", "black", "--filter-files"]
13 |
14 | - repo: https://github.com/pre-commit/pre-commit-hooks
15 | rev: v4.4.0
16 | hooks:
17 | - id: trailing-whitespace
18 | exclude: ^docs/.*|.*.md
19 | - id: end-of-file-fixer
20 | exclude: ^docs/.*|.*.md
21 |
22 | - repo: https://github.com/psf/black
23 | rev: 23.3.0
24 | hooks:
25 | - id: black
26 | language_version: python3
27 |
--------------------------------------------------------------------------------
/.pypirc:
--------------------------------------------------------------------------------
1 | [distutils]
2 | index-servers =
3 | pypi
4 |
5 | [pypi]
6 | repository: https://upload.pypi.org/legacy/
7 |
--------------------------------------------------------------------------------
/.pyup.yml:
--------------------------------------------------------------------------------
1 | # autogenerated pyup.io config file
2 | # see https://pyup.io/docs/configuration/ for all available options
3 |
4 | schedule: ''
5 | update: False
6 |
--------------------------------------------------------------------------------
/.whitesource:
--------------------------------------------------------------------------------
1 | {
2 | "scanSettings": {
3 | "baseBranches": []
4 | },
5 | "checkRunSettings": {
6 | "vulnerableCheckRunConclusionLevel": "failure",
7 | "displayMode": "diff"
8 | },
9 | "issueSettings": {
10 | "minSeverityLevel": "LOW"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [v1.2.3](https://github.com/tj-django/django-extra-field-validation/tree/v1.2.3) (2023-01-14)
4 |
5 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.2.2...v1.2.3)
6 |
7 | **Merged pull requests:**
8 |
9 | - Upgraded v1.2.1 → v1.2.2 [\#308](https://github.com/tj-django/django-extra-field-validation/pull/308) ([jackton1](https://github.com/jackton1))
10 |
11 | ## [v1.2.2](https://github.com/tj-django/django-extra-field-validation/tree/v1.2.2) (2023-01-14)
12 |
13 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.2.1...v1.2.2)
14 |
15 | ## [v1.2.1](https://github.com/tj-django/django-extra-field-validation/tree/v1.2.1) (2023-01-14)
16 |
17 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.2.0...v1.2.1)
18 |
19 | **Merged pull requests:**
20 |
21 | - chore: update Makefile [\#306](https://github.com/tj-django/django-extra-field-validation/pull/306) ([jackton1](https://github.com/jackton1))
22 | - Update CHANGELOG [\#305](https://github.com/tj-django/django-extra-field-validation/pull/305) ([jackton1](https://github.com/jackton1))
23 |
24 | ## [v1.2.0](https://github.com/tj-django/django-extra-field-validation/tree/v1.2.0) (2023-01-13)
25 |
26 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.1.1...v1.2.0)
27 |
28 | **Implemented enhancements:**
29 |
30 | - \[Feature\] Drop support for python 2.7 [\#186](https://github.com/tj-django/django-extra-field-validation/issues/186)
31 |
32 | **Closed issues:**
33 |
34 | - CVE-2022-40899 \(High\) detected in future-0.18.2.tar.gz - autoclosed [\#293](https://github.com/tj-django/django-extra-field-validation/issues/293)
35 | - Action Required: Fix Renovate Configuration [\#176](https://github.com/tj-django/django-extra-field-validation/issues/176)
36 | - Initial Update [\#160](https://github.com/tj-django/django-extra-field-validation/issues/160)
37 | - Dependency Dashboard [\#135](https://github.com/tj-django/django-extra-field-validation/issues/135)
38 | - Clean up bumpversion match to use just the version. [\#107](https://github.com/tj-django/django-extra-field-validation/issues/107)
39 | - Convert to github actions. [\#95](https://github.com/tj-django/django-extra-field-validation/issues/95)
40 |
41 | **Merged pull requests:**
42 |
43 | - Update dependency future to v0.18.3 [\#303](https://github.com/tj-django/django-extra-field-validation/pull/303) ([renovate[bot]](https://github.com/apps/renovate))
44 | - Update actions/setup-python action to v4.5.0 [\#302](https://github.com/tj-django/django-extra-field-validation/pull/302) ([renovate[bot]](https://github.com/apps/renovate))
45 | - chore: update packages [\#301](https://github.com/tj-django/django-extra-field-validation/pull/301) ([jackton1](https://github.com/jackton1))
46 | - Updated docs [\#300](https://github.com/tj-django/django-extra-field-validation/pull/300) ([github-actions[bot]](https://github.com/apps/github-actions))
47 | - Update actions/cache action to v3.2.3 [\#299](https://github.com/tj-django/django-extra-field-validation/pull/299) ([renovate[bot]](https://github.com/apps/renovate))
48 | - Update actions/checkout action to v3.3.0 [\#298](https://github.com/tj-django/django-extra-field-validation/pull/298) ([renovate[bot]](https://github.com/apps/renovate))
49 | - Update peaceiris/actions-gh-pages action to v3.9.1 [\#297](https://github.com/tj-django/django-extra-field-validation/pull/297) ([renovate[bot]](https://github.com/apps/renovate))
50 | - Update wearerequired/lint-action action to v2.2.0 [\#296](https://github.com/tj-django/django-extra-field-validation/pull/296) ([renovate[bot]](https://github.com/apps/renovate))
51 | - Update tj-actions/github-changelog-generator action to v1.17 [\#295](https://github.com/tj-django/django-extra-field-validation/pull/295) ([renovate[bot]](https://github.com/apps/renovate))
52 | - Update actions/cache action to v3.2.2 [\#294](https://github.com/tj-django/django-extra-field-validation/pull/294) ([renovate[bot]](https://github.com/apps/renovate))
53 | - Update actions/cache action to v3.2.1 [\#292](https://github.com/tj-django/django-extra-field-validation/pull/292) ([renovate[bot]](https://github.com/apps/renovate))
54 | - Update actions/setup-python action to v4.4.0 [\#291](https://github.com/tj-django/django-extra-field-validation/pull/291) ([renovate[bot]](https://github.com/apps/renovate))
55 | - Bump actions/cache from 3.0.11 to 3.2.0 [\#290](https://github.com/tj-django/django-extra-field-validation/pull/290) ([dependabot[bot]](https://github.com/apps/dependabot))
56 | - Update actions/cache action to v3.2.0 [\#289](https://github.com/tj-django/django-extra-field-validation/pull/289) ([renovate[bot]](https://github.com/apps/renovate))
57 | - Update tj-actions/verify-changed-files action to v13 [\#288](https://github.com/tj-django/django-extra-field-validation/pull/288) ([renovate[bot]](https://github.com/apps/renovate))
58 | - Update actions/checkout action to v3.2.0 [\#286](https://github.com/tj-django/django-extra-field-validation/pull/286) ([renovate[bot]](https://github.com/apps/renovate))
59 | - Update actions/setup-python action to v4.3.1 [\#285](https://github.com/tj-django/django-extra-field-validation/pull/285) ([renovate[bot]](https://github.com/apps/renovate))
60 | - Add CodeQL workflow for GitHub code scanning [\#283](https://github.com/tj-django/django-extra-field-validation/pull/283) ([lgtm-com[bot]](https://github.com/apps/lgtm-com))
61 | - Update peter-evans/create-pull-request action to v4.2.3 [\#282](https://github.com/tj-django/django-extra-field-validation/pull/282) ([renovate[bot]](https://github.com/apps/renovate))
62 | - Update peter-evans/create-pull-request action to v4.2.2 [\#281](https://github.com/tj-django/django-extra-field-validation/pull/281) ([renovate[bot]](https://github.com/apps/renovate))
63 | - Update peter-evans/create-pull-request action to v4.2.1 [\#280](https://github.com/tj-django/django-extra-field-validation/pull/280) ([renovate[bot]](https://github.com/apps/renovate))
64 | - Bump hmarr/auto-approve-action from 2 to 3 [\#279](https://github.com/tj-django/django-extra-field-validation/pull/279) ([dependabot[bot]](https://github.com/apps/dependabot))
65 | - Update peaceiris/actions-gh-pages action to v3.9.0 [\#278](https://github.com/tj-django/django-extra-field-validation/pull/278) ([renovate[bot]](https://github.com/apps/renovate))
66 | - Update peter-evans/create-pull-request action to v4.2.0 [\#277](https://github.com/tj-django/django-extra-field-validation/pull/277) ([renovate[bot]](https://github.com/apps/renovate))
67 | - Update pascalgn/automerge-action action to v0.15.5 [\#276](https://github.com/tj-django/django-extra-field-validation/pull/276) ([renovate[bot]](https://github.com/apps/renovate))
68 | - Update peter-evans/create-pull-request action to v4.1.4 [\#275](https://github.com/tj-django/django-extra-field-validation/pull/275) ([renovate[bot]](https://github.com/apps/renovate))
69 | - Update tj-actions/verify-changed-files action to v12 [\#274](https://github.com/tj-django/django-extra-field-validation/pull/274) ([renovate[bot]](https://github.com/apps/renovate))
70 | - Update actions/cache action to v3.0.11 [\#273](https://github.com/tj-django/django-extra-field-validation/pull/273) ([renovate[bot]](https://github.com/apps/renovate))
71 | - Update actions/setup-python action to v4.3.0 [\#272](https://github.com/tj-django/django-extra-field-validation/pull/272) ([renovate[bot]](https://github.com/apps/renovate))
72 | - Update actions/first-interaction action to v1.1.1 [\#271](https://github.com/tj-django/django-extra-field-validation/pull/271) ([renovate[bot]](https://github.com/apps/renovate))
73 | - Update actions/checkout action to v3.1.0 [\#270](https://github.com/tj-django/django-extra-field-validation/pull/270) ([renovate[bot]](https://github.com/apps/renovate))
74 | - Update actions/cache action to v3.0.10 [\#269](https://github.com/tj-django/django-extra-field-validation/pull/269) ([renovate[bot]](https://github.com/apps/renovate))
75 | - Update actions/cache action to v3.0.9 [\#268](https://github.com/tj-django/django-extra-field-validation/pull/268) ([renovate[bot]](https://github.com/apps/renovate))
76 | - Update peter-evans/create-pull-request action to v4.1.3 [\#267](https://github.com/tj-django/django-extra-field-validation/pull/267) ([renovate[bot]](https://github.com/apps/renovate))
77 | - Update peter-evans/create-pull-request action to v4.1.2 [\#266](https://github.com/tj-django/django-extra-field-validation/pull/266) ([renovate[bot]](https://github.com/apps/renovate))
78 | - Bump codacy/codacy-analysis-cli-action from 4.1.0 to 4.2.0 [\#265](https://github.com/tj-django/django-extra-field-validation/pull/265) ([dependabot[bot]](https://github.com/apps/dependabot))
79 | - \[pre-commit.ci\] pre-commit autoupdate [\#264](https://github.com/tj-django/django-extra-field-validation/pull/264) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
80 | - Update tj-actions/github-changelog-generator action to v1.15 [\#263](https://github.com/tj-django/django-extra-field-validation/pull/263) ([renovate[bot]](https://github.com/apps/renovate))
81 | - Bump wearerequired/lint-action from 2.0.1 to 2.1.0 [\#262](https://github.com/tj-django/django-extra-field-validation/pull/262) ([dependabot[bot]](https://github.com/apps/dependabot))
82 | - Update tj-actions/verify-changed-files action to v11 [\#261](https://github.com/tj-django/django-extra-field-validation/pull/261) ([renovate[bot]](https://github.com/apps/renovate))
83 | - Update actions/cache action to v3.0.8 [\#260](https://github.com/tj-django/django-extra-field-validation/pull/260) ([renovate[bot]](https://github.com/apps/renovate))
84 | - Update peter-evans/create-pull-request action to v4.1.1 [\#259](https://github.com/tj-django/django-extra-field-validation/pull/259) ([renovate[bot]](https://github.com/apps/renovate))
85 | - Update peter-evans/create-pull-request action to v4.1.0 [\#258](https://github.com/tj-django/django-extra-field-validation/pull/258) ([renovate[bot]](https://github.com/apps/renovate))
86 | - Update actions/cache action to v3.0.7 [\#257](https://github.com/tj-django/django-extra-field-validation/pull/257) ([renovate[bot]](https://github.com/apps/renovate))
87 | - Update actions/cache action to v3.0.6 [\#256](https://github.com/tj-django/django-extra-field-validation/pull/256) ([renovate[bot]](https://github.com/apps/renovate))
88 | - Update actions/setup-python action to v4.2.0 [\#255](https://github.com/tj-django/django-extra-field-validation/pull/255) ([renovate[bot]](https://github.com/apps/renovate))
89 | - Update actions/cache action to v3.0.5 [\#254](https://github.com/tj-django/django-extra-field-validation/pull/254) ([renovate[bot]](https://github.com/apps/renovate))
90 | - Bump actions/setup-python from 4.0.0 to 4.1.0 [\#253](https://github.com/tj-django/django-extra-field-validation/pull/253) ([dependabot[bot]](https://github.com/apps/dependabot))
91 | - Update wearerequired/lint-action action to v2.0.1 [\#252](https://github.com/tj-django/django-extra-field-validation/pull/252) ([renovate[bot]](https://github.com/apps/renovate))
92 | - Update tj-actions/github-changelog-generator action to v1.14 [\#251](https://github.com/tj-django/django-extra-field-validation/pull/251) ([renovate[bot]](https://github.com/apps/renovate))
93 | - Update tj-actions/verify-changed-files action to v10 [\#250](https://github.com/tj-django/django-extra-field-validation/pull/250) ([renovate[bot]](https://github.com/apps/renovate))
94 | - Update codacy/codacy-analysis-cli-action action to v4.1.0 [\#249](https://github.com/tj-django/django-extra-field-validation/pull/249) ([renovate[bot]](https://github.com/apps/renovate))
95 | - \[pre-commit.ci\] pre-commit autoupdate [\#248](https://github.com/tj-django/django-extra-field-validation/pull/248) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
96 | - Bump actions/setup-python from 3.1.2 to 4.0.0 [\#247](https://github.com/tj-django/django-extra-field-validation/pull/247) ([dependabot[bot]](https://github.com/apps/dependabot))
97 | - Update actions/cache action to v3.0.4 [\#246](https://github.com/tj-django/django-extra-field-validation/pull/246) ([renovate[bot]](https://github.com/apps/renovate))
98 | - Update peter-evans/create-pull-request action to v4.0.4 [\#245](https://github.com/tj-django/django-extra-field-validation/pull/245) ([renovate[bot]](https://github.com/apps/renovate))
99 | - Update actions/cache action to v3.0.3 [\#244](https://github.com/tj-django/django-extra-field-validation/pull/244) ([renovate[bot]](https://github.com/apps/renovate))
100 | - Update pascalgn/automerge-action action to v0.15.3 [\#243](https://github.com/tj-django/django-extra-field-validation/pull/243) ([renovate[bot]](https://github.com/apps/renovate))
101 | - Update peter-evans/create-pull-request action to v4.0.3 [\#242](https://github.com/tj-django/django-extra-field-validation/pull/242) ([renovate[bot]](https://github.com/apps/renovate))
102 | - Update wearerequired/lint-action action to v2 [\#241](https://github.com/tj-django/django-extra-field-validation/pull/241) ([renovate[bot]](https://github.com/apps/renovate))
103 | - Update github/codeql-action action to v2 [\#240](https://github.com/tj-django/django-extra-field-validation/pull/240) ([renovate[bot]](https://github.com/apps/renovate))
104 | - Update actions/checkout action to v3.0.2 [\#238](https://github.com/tj-django/django-extra-field-validation/pull/238) ([renovate[bot]](https://github.com/apps/renovate))
105 | - Update actions/checkout action to v3.0.1 [\#237](https://github.com/tj-django/django-extra-field-validation/pull/237) ([renovate[bot]](https://github.com/apps/renovate))
106 | - Update actions/setup-python action to v3.1.2 [\#236](https://github.com/tj-django/django-extra-field-validation/pull/236) ([renovate[bot]](https://github.com/apps/renovate))
107 | - Update actions/cache action to v3.0.2 [\#235](https://github.com/tj-django/django-extra-field-validation/pull/235) ([renovate[bot]](https://github.com/apps/renovate))
108 | - Update peter-evans/create-pull-request action to v4.0.2 [\#234](https://github.com/tj-django/django-extra-field-validation/pull/234) ([renovate[bot]](https://github.com/apps/renovate))
109 | - Update actions/setup-python action to v3.1.1 [\#233](https://github.com/tj-django/django-extra-field-validation/pull/233) ([renovate[bot]](https://github.com/apps/renovate))
110 | - Update actions/setup-python action to v3.1.0 [\#232](https://github.com/tj-django/django-extra-field-validation/pull/232) ([renovate[bot]](https://github.com/apps/renovate))
111 | - Update peter-evans/create-pull-request action to v4.0.1 [\#231](https://github.com/tj-django/django-extra-field-validation/pull/231) ([renovate[bot]](https://github.com/apps/renovate))
112 | - Update actions/cache action to v3.0.1 [\#230](https://github.com/tj-django/django-extra-field-validation/pull/230) ([renovate[bot]](https://github.com/apps/renovate))
113 | - \[pre-commit.ci\] pre-commit autoupdate [\#229](https://github.com/tj-django/django-extra-field-validation/pull/229) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
114 | - Update pascalgn/automerge-action action to v0.15.2 [\#228](https://github.com/tj-django/django-extra-field-validation/pull/228) ([renovate[bot]](https://github.com/apps/renovate))
115 | - Update pascalgn/automerge-action action to v0.14.4 [\#227](https://github.com/tj-django/django-extra-field-validation/pull/227) ([renovate[bot]](https://github.com/apps/renovate))
116 | - Update peter-evans/create-pull-request action [\#226](https://github.com/tj-django/django-extra-field-validation/pull/226) ([renovate[bot]](https://github.com/apps/renovate))
117 | - Updated docs [\#225](https://github.com/tj-django/django-extra-field-validation/pull/225) ([github-actions[bot]](https://github.com/apps/github-actions))
118 | - Update wearerequired/lint-action action to v1.12.0 [\#224](https://github.com/tj-django/django-extra-field-validation/pull/224) ([renovate[bot]](https://github.com/apps/renovate))
119 | - Update actions/cache action to v3 [\#223](https://github.com/tj-django/django-extra-field-validation/pull/223) ([renovate[bot]](https://github.com/apps/renovate))
120 | - Updated README.md [\#222](https://github.com/tj-django/django-extra-field-validation/pull/222) ([jackton1](https://github.com/jackton1))
121 | - Bump tj-actions/remark from 2.3 to 3 [\#221](https://github.com/tj-django/django-extra-field-validation/pull/221) ([dependabot[bot]](https://github.com/apps/dependabot))
122 | - Update tj-actions/github-changelog-generator action to v1.13 [\#220](https://github.com/tj-django/django-extra-field-validation/pull/220) ([renovate[bot]](https://github.com/apps/renovate))
123 | - Update tj-actions/verify-changed-files action to v9 [\#219](https://github.com/tj-django/django-extra-field-validation/pull/219) ([renovate[bot]](https://github.com/apps/renovate))
124 | - Update codacy/codacy-analysis-cli-action action to v4.0.2 [\#218](https://github.com/tj-django/django-extra-field-validation/pull/218) ([renovate[bot]](https://github.com/apps/renovate))
125 | - Update codacy/codacy-analysis-cli-action action to v4.0.1 [\#217](https://github.com/tj-django/django-extra-field-validation/pull/217) ([renovate[bot]](https://github.com/apps/renovate))
126 | - Update actions/checkout action [\#216](https://github.com/tj-django/django-extra-field-validation/pull/216) ([renovate[bot]](https://github.com/apps/renovate))
127 | - Update peter-evans/create-pull-request action to v3.14.0 [\#215](https://github.com/tj-django/django-extra-field-validation/pull/215) ([renovate[bot]](https://github.com/apps/renovate))
128 | - Update actions/setup-python action to v3 [\#214](https://github.com/tj-django/django-extra-field-validation/pull/214) ([renovate[bot]](https://github.com/apps/renovate))
129 | - Update peter-evans/create-pull-request action to v3.13.0 [\#213](https://github.com/tj-django/django-extra-field-validation/pull/213) ([renovate[bot]](https://github.com/apps/renovate))
130 | - Update tj-actions/github-changelog-generator action to v1.12 [\#212](https://github.com/tj-django/django-extra-field-validation/pull/212) ([renovate[bot]](https://github.com/apps/renovate))
131 | - Update actions/setup-python action to v2.3.2 [\#211](https://github.com/tj-django/django-extra-field-validation/pull/211) ([renovate[bot]](https://github.com/apps/renovate))
132 | - \[pre-commit.ci\] pre-commit autoupdate [\#210](https://github.com/tj-django/django-extra-field-validation/pull/210) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
133 | - Update peter-evans/create-pull-request action to v3.12.1 [\#209](https://github.com/tj-django/django-extra-field-validation/pull/209) ([renovate[bot]](https://github.com/apps/renovate))
134 | - Updated docs [\#208](https://github.com/tj-django/django-extra-field-validation/pull/208) ([github-actions[bot]](https://github.com/apps/github-actions))
135 | - Update wearerequired/lint-action action to v1.11.1 [\#207](https://github.com/tj-django/django-extra-field-validation/pull/207) ([renovate[bot]](https://github.com/apps/renovate))
136 | - Update wearerequired/lint-action action to v1.11.0 [\#206](https://github.com/tj-django/django-extra-field-validation/pull/206) ([renovate[bot]](https://github.com/apps/renovate))
137 | - Update tj-actions/remark action to v2.3 [\#205](https://github.com/tj-django/django-extra-field-validation/pull/205) ([renovate[bot]](https://github.com/apps/renovate))
138 | - Update tj-actions/remark action to v2.2 [\#204](https://github.com/tj-django/django-extra-field-validation/pull/204) ([renovate[bot]](https://github.com/apps/renovate))
139 | - Update tj-actions/remark action to v2 [\#203](https://github.com/tj-django/django-extra-field-validation/pull/203) ([renovate[bot]](https://github.com/apps/renovate))
140 | - Update tj-actions/github-changelog-generator action to v1.11 [\#202](https://github.com/tj-django/django-extra-field-validation/pull/202) ([renovate[bot]](https://github.com/apps/renovate))
141 | - Removed unused code [\#201](https://github.com/tj-django/django-extra-field-validation/pull/201) ([jackton1](https://github.com/jackton1))
142 | - Updated docs [\#200](https://github.com/tj-django/django-extra-field-validation/pull/200) ([github-actions[bot]](https://github.com/apps/github-actions))
143 | - Updated README.md [\#199](https://github.com/tj-django/django-extra-field-validation/pull/199) ([jackton1](https://github.com/jackton1))
144 | - Updated README.md [\#198](https://github.com/tj-django/django-extra-field-validation/pull/198) ([jackton1](https://github.com/jackton1))
145 | - Updated docs [\#197](https://github.com/tj-django/django-extra-field-validation/pull/197) ([github-actions[bot]](https://github.com/apps/github-actions))
146 | - Updated docs [\#196](https://github.com/tj-django/django-extra-field-validation/pull/196) ([github-actions[bot]](https://github.com/apps/github-actions))
147 | - Updated README.md [\#195](https://github.com/tj-django/django-extra-field-validation/pull/195) ([jackton1](https://github.com/jackton1))
148 | - Updated README.md [\#194](https://github.com/tj-django/django-extra-field-validation/pull/194) ([jackton1](https://github.com/jackton1))
149 | - Updated docs [\#193](https://github.com/tj-django/django-extra-field-validation/pull/193) ([github-actions[bot]](https://github.com/apps/github-actions))
150 | - Updated README.md [\#192](https://github.com/tj-django/django-extra-field-validation/pull/192) ([jackton1](https://github.com/jackton1))
151 | - Updated docs [\#191](https://github.com/tj-django/django-extra-field-validation/pull/191) ([github-actions[bot]](https://github.com/apps/github-actions))
152 | - Updated docs [\#190](https://github.com/tj-django/django-extra-field-validation/pull/190) ([github-actions[bot]](https://github.com/apps/github-actions))
153 | - Update tj-actions/github-changelog-generator action to v1.10 [\#189](https://github.com/tj-django/django-extra-field-validation/pull/189) ([renovate[bot]](https://github.com/apps/renovate))
154 | - \[pre-commit.ci\] pre-commit autoupdate [\#188](https://github.com/tj-django/django-extra-field-validation/pull/188) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
155 | - Update peter-evans/create-pull-request action to v3.12.0 [\#187](https://github.com/tj-django/django-extra-field-validation/pull/187) ([renovate[bot]](https://github.com/apps/renovate))
156 | - \[pre-commit.ci\] pre-commit autoupdate [\#185](https://github.com/tj-django/django-extra-field-validation/pull/185) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
157 | - Update actions/setup-python action to v2.3.1 [\#184](https://github.com/tj-django/django-extra-field-validation/pull/184) ([renovate[bot]](https://github.com/apps/renovate))
158 | - Update actions/cache action to v2.1.7 [\#183](https://github.com/tj-django/django-extra-field-validation/pull/183) ([renovate[bot]](https://github.com/apps/renovate))
159 | - \[pre-commit.ci\] pre-commit autoupdate [\#182](https://github.com/tj-django/django-extra-field-validation/pull/182) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
160 | - Update actions/setup-python action to v2.3.0 [\#181](https://github.com/tj-django/django-extra-field-validation/pull/181) ([renovate[bot]](https://github.com/apps/renovate))
161 | - \[pre-commit.ci\] pre-commit autoupdate [\#180](https://github.com/tj-django/django-extra-field-validation/pull/180) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
162 | - Update peter-evans/create-pull-request action to v3.11.0 [\#179](https://github.com/tj-django/django-extra-field-validation/pull/179) ([renovate[bot]](https://github.com/apps/renovate))
163 | - Update actions/checkout action to v2.4.0 [\#178](https://github.com/tj-django/django-extra-field-validation/pull/178) ([renovate[bot]](https://github.com/apps/renovate))
164 | - \[pre-commit.ci\] pre-commit autoupdate [\#177](https://github.com/tj-django/django-extra-field-validation/pull/177) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
165 | - Update actions/checkout action to v2.3.5 [\#175](https://github.com/tj-django/django-extra-field-validation/pull/175) ([renovate[bot]](https://github.com/apps/renovate))
166 | - \[pre-commit.ci\] pre-commit autoupdate [\#174](https://github.com/tj-django/django-extra-field-validation/pull/174) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
167 | - Update pascalgn/automerge-action action to v0.14.3 [\#173](https://github.com/tj-django/django-extra-field-validation/pull/173) ([renovate[bot]](https://github.com/apps/renovate))
168 | - \[pre-commit.ci\] pre-commit autoupdate [\#172](https://github.com/tj-django/django-extra-field-validation/pull/172) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
169 | - Update peter-evans/create-pull-request action to v3.10.1 [\#171](https://github.com/tj-django/django-extra-field-validation/pull/171) ([renovate[bot]](https://github.com/apps/renovate))
170 | - Update precommit hook pycqa/isort to v5.9.3 [\#170](https://github.com/tj-django/django-extra-field-validation/pull/170) ([renovate[bot]](https://github.com/apps/renovate))
171 | - Update codacy/codacy-analysis-cli-action action to v4 [\#169](https://github.com/tj-django/django-extra-field-validation/pull/169) ([renovate[bot]](https://github.com/apps/renovate))
172 | - \[pre-commit.ci\] pre-commit autoupdate [\#168](https://github.com/tj-django/django-extra-field-validation/pull/168) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
173 | - Update codacy/codacy-analysis-cli-action action to v3 [\#167](https://github.com/tj-django/django-extra-field-validation/pull/167) ([renovate[bot]](https://github.com/apps/renovate))
174 | - Update precommit hook pycqa/isort to v5.9.2 [\#166](https://github.com/tj-django/django-extra-field-validation/pull/166) ([renovate[bot]](https://github.com/apps/renovate))
175 | - Update precommit hook pycqa/isort to v5.9.1 [\#164](https://github.com/tj-django/django-extra-field-validation/pull/164) ([renovate[bot]](https://github.com/apps/renovate))
176 | - Update precommit hook pycqa/isort to v5.9.0 [\#163](https://github.com/tj-django/django-extra-field-validation/pull/163) ([renovate[bot]](https://github.com/apps/renovate))
177 | - \[pre-commit.ci\] pre-commit autoupdate [\#162](https://github.com/tj-django/django-extra-field-validation/pull/162) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
178 | - Config file for pyup.io [\#161](https://github.com/tj-django/django-extra-field-validation/pull/161) ([pyup-bot](https://github.com/pyup-bot))
179 | - Updated docs [\#159](https://github.com/tj-django/django-extra-field-validation/pull/159) ([github-actions[bot]](https://github.com/apps/github-actions))
180 | - Update README.md [\#158](https://github.com/tj-django/django-extra-field-validation/pull/158) ([jackton1](https://github.com/jackton1))
181 | - Update wearerequired/lint-action action to v1.10.0 [\#156](https://github.com/tj-django/django-extra-field-validation/pull/156) ([renovate[bot]](https://github.com/apps/renovate))
182 | - Update actions/cache action to v2.1.6 [\#155](https://github.com/tj-django/django-extra-field-validation/pull/155) ([renovate[bot]](https://github.com/apps/renovate))
183 | - Update pascalgn/automerge-action action to v0.14.2 [\#154](https://github.com/tj-django/django-extra-field-validation/pull/154) ([renovate[bot]](https://github.com/apps/renovate))
184 | - Bump peter-evans/create-pull-request from 3.9.2 to 3.10.0 [\#153](https://github.com/tj-django/django-extra-field-validation/pull/153) ([dependabot[bot]](https://github.com/apps/dependabot))
185 | - Update precommit hook pre-commit/pre-commit-hooks to v4.0.1 [\#152](https://github.com/tj-django/django-extra-field-validation/pull/152) ([renovate[bot]](https://github.com/apps/renovate))
186 | - Update precommit hook pre-commit/pre-commit-hooks to v4 [\#151](https://github.com/tj-django/django-extra-field-validation/pull/151) ([renovate[bot]](https://github.com/apps/renovate))
187 | - Bump peter-evans/create-pull-request from 3.9.1 to 3.9.2 [\#150](https://github.com/tj-django/django-extra-field-validation/pull/150) ([dependabot[bot]](https://github.com/apps/dependabot))
188 | - Bump actions/setup-python from 2 to 2.2.2 [\#148](https://github.com/tj-django/django-extra-field-validation/pull/148) ([dependabot[bot]](https://github.com/apps/dependabot))
189 | - Bump peter-evans/create-pull-request from 3 to 3.9.1 [\#147](https://github.com/tj-django/django-extra-field-validation/pull/147) ([dependabot[bot]](https://github.com/apps/dependabot))
190 | - Bump actions/cache from 2 to 2.1.5 [\#146](https://github.com/tj-django/django-extra-field-validation/pull/146) ([dependabot[bot]](https://github.com/apps/dependabot))
191 | - Bump actions/first-interaction from 1 to 1.1.0 [\#145](https://github.com/tj-django/django-extra-field-validation/pull/145) ([dependabot[bot]](https://github.com/apps/dependabot))
192 | - Bump actions/checkout from 2 to 2.3.4 [\#144](https://github.com/tj-django/django-extra-field-validation/pull/144) ([dependabot[bot]](https://github.com/apps/dependabot))
193 | - Bump peaceiris/actions-gh-pages from 3 to 3.8.0 [\#143](https://github.com/tj-django/django-extra-field-validation/pull/143) ([dependabot[bot]](https://github.com/apps/dependabot))
194 | - \[pre-commit.ci\] pre-commit autoupdate [\#141](https://github.com/tj-django/django-extra-field-validation/pull/141) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
195 | - Update dependency six to v1.16.0 [\#140](https://github.com/tj-django/django-extra-field-validation/pull/140) ([renovate[bot]](https://github.com/apps/renovate))
196 | - Update tj-actions/github-changelog-generator action to v1.8 [\#139](https://github.com/tj-django/django-extra-field-validation/pull/139) ([renovate[bot]](https://github.com/apps/renovate))
197 | - \[pre-commit.ci\] pre-commit autoupdate [\#138](https://github.com/tj-django/django-extra-field-validation/pull/138) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
198 | - Upgrade to GitHub-native Dependabot [\#137](https://github.com/tj-django/django-extra-field-validation/pull/137) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
199 | - \[pre-commit.ci\] pre-commit autoupdate [\#136](https://github.com/tj-django/django-extra-field-validation/pull/136) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
200 | - Update tj-actions/github-changelog-generator action to v1.6 [\#134](https://github.com/tj-django/django-extra-field-validation/pull/134) ([renovate[bot]](https://github.com/apps/renovate))
201 | - Updated docs [\#133](https://github.com/tj-django/django-extra-field-validation/pull/133) ([github-actions[bot]](https://github.com/apps/github-actions))
202 | - Updated docs [\#132](https://github.com/tj-django/django-extra-field-validation/pull/132) ([github-actions[bot]](https://github.com/apps/github-actions))
203 | - Update README.md [\#131](https://github.com/tj-django/django-extra-field-validation/pull/131) ([jackton1](https://github.com/jackton1))
204 | - Updated docs [\#129](https://github.com/tj-django/django-extra-field-validation/pull/129) ([github-actions[bot]](https://github.com/apps/github-actions))
205 | - Updated docs [\#128](https://github.com/tj-django/django-extra-field-validation/pull/128) ([github-actions[bot]](https://github.com/apps/github-actions))
206 | - Create .fussyfox.yml [\#127](https://github.com/tj-django/django-extra-field-validation/pull/127) ([jackton1](https://github.com/jackton1))
207 | - Update test.yml [\#126](https://github.com/tj-django/django-extra-field-validation/pull/126) ([jackton1](https://github.com/jackton1))
208 | - Update CHANGELOG [\#124](https://github.com/tj-django/django-extra-field-validation/pull/124) ([jackton1](https://github.com/jackton1))
209 | - Updated docs [\#122](https://github.com/tj-django/django-extra-field-validation/pull/122) ([github-actions[bot]](https://github.com/apps/github-actions))
210 | - Update CHANGELOG [\#121](https://github.com/tj-django/django-extra-field-validation/pull/121) ([jackton1](https://github.com/jackton1))
211 |
212 | ## [v1.1.1](https://github.com/tj-django/django-extra-field-validation/tree/v1.1.1) (2021-03-21)
213 |
214 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.1.0...v1.1.1)
215 |
216 | ## [v1.1.0](https://github.com/tj-django/django-extra-field-validation/tree/v1.1.0) (2021-03-21)
217 |
218 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.0.2...v1.1.0)
219 |
220 | **Merged pull requests:**
221 |
222 | - Updated docs [\#120](https://github.com/tj-django/django-extra-field-validation/pull/120) ([github-actions[bot]](https://github.com/apps/github-actions))
223 |
224 | ## [v1.0.2](https://github.com/tj-django/django-extra-field-validation/tree/v1.0.2) (2021-03-20)
225 |
226 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.0.1...v1.0.2)
227 |
228 | ## [v1.0.1](https://github.com/tj-django/django-extra-field-validation/tree/v1.0.1) (2021-03-20)
229 |
230 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v1.0.0...v1.0.1)
231 |
232 | **Closed issues:**
233 |
234 | - Fix README adding links to sections [\#110](https://github.com/tj-django/django-extra-field-validation/issues/110)
235 |
236 | **Merged pull requests:**
237 |
238 | - Updated docs [\#118](https://github.com/tj-django/django-extra-field-validation/pull/118) ([github-actions[bot]](https://github.com/apps/github-actions))
239 | - Updated docs [\#117](https://github.com/tj-django/django-extra-field-validation/pull/117) ([github-actions[bot]](https://github.com/apps/github-actions))
240 | - Update wearerequired/lint-action action to v1.9.0 [\#116](https://github.com/tj-django/django-extra-field-validation/pull/116) ([renovate[bot]](https://github.com/apps/renovate))
241 | - Updated docs [\#115](https://github.com/tj-django/django-extra-field-validation/pull/115) ([github-actions[bot]](https://github.com/apps/github-actions))
242 | - Update wearerequired/lint-action action to v1.8.0 [\#114](https://github.com/tj-django/django-extra-field-validation/pull/114) ([renovate[bot]](https://github.com/apps/renovate))
243 | - Update dependency bump2version to v1 [\#113](https://github.com/tj-django/django-extra-field-validation/pull/113) ([renovate[bot]](https://github.com/apps/renovate))
244 | - Updated docs [\#112](https://github.com/tj-django/django-extra-field-validation/pull/112) ([github-actions[bot]](https://github.com/apps/github-actions))
245 | - Update CHANGELOG [\#111](https://github.com/tj-django/django-extra-field-validation/pull/111) ([jackton1](https://github.com/jackton1))
246 |
247 | ## [v1.0.0](https://github.com/tj-django/django-extra-field-validation/tree/v1.0.0) (2021-03-03)
248 |
249 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.2.2...v1.0.0)
250 |
251 | ## [v0.2.2](https://github.com/tj-django/django-extra-field-validation/tree/v0.2.2) (2021-03-03)
252 |
253 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.2.0...v0.2.2)
254 |
255 | **Merged pull requests:**
256 |
257 | - Updated docs [\#109](https://github.com/tj-django/django-extra-field-validation/pull/109) ([github-actions[bot]](https://github.com/apps/github-actions))
258 | - Update to use bump2version [\#108](https://github.com/tj-django/django-extra-field-validation/pull/108) ([jackton1](https://github.com/jackton1))
259 | - Increased test coverage and update tox config. [\#106](https://github.com/tj-django/django-extra-field-validation/pull/106) ([jackton1](https://github.com/jackton1))
260 | - Update CHANGELOG [\#105](https://github.com/tj-django/django-extra-field-validation/pull/105) ([jackton1](https://github.com/jackton1))
261 |
262 | ## [v0.2.0](https://github.com/tj-django/django-extra-field-validation/tree/v0.2.0) (2021-02-25)
263 |
264 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.13...v0.2.0)
265 |
266 | **Closed issues:**
267 |
268 | - Add black to auto fix lint errors. [\#99](https://github.com/tj-django/django-extra-field-validation/issues/99)
269 | - Update usage of ugettext to gettext to fix Deprecation warnings. [\#90](https://github.com/tj-django/django-extra-field-validation/issues/90)
270 |
271 | **Merged pull requests:**
272 |
273 | - Added support for generating CHANGELOG.md [\#104](https://github.com/tj-django/django-extra-field-validation/pull/104) ([jackton1](https://github.com/jackton1))
274 | - Updated docs [\#103](https://github.com/tj-django/django-extra-field-validation/pull/103) ([github-actions[bot]](https://github.com/apps/github-actions))
275 | - Update README.md [\#102](https://github.com/tj-django/django-extra-field-validation/pull/102) ([jackton1](https://github.com/jackton1))
276 | - Added docs. [\#101](https://github.com/tj-django/django-extra-field-validation/pull/101) ([jackton1](https://github.com/jackton1))
277 | - Update README.md [\#100](https://github.com/tj-django/django-extra-field-validation/pull/100) ([jackton1](https://github.com/jackton1))
278 | - Increase test coverage. [\#98](https://github.com/tj-django/django-extra-field-validation/pull/98) ([jackton1](https://github.com/jackton1))
279 | - Update and rename README.rst to README.md [\#97](https://github.com/tj-django/django-extra-field-validation/pull/97) ([jackton1](https://github.com/jackton1))
280 | - Fixed test [\#96](https://github.com/tj-django/django-extra-field-validation/pull/96) ([jackton1](https://github.com/jackton1))
281 | - Feature/resolve deprecation warning [\#94](https://github.com/tj-django/django-extra-field-validation/pull/94) ([jackton1](https://github.com/jackton1))
282 | - Develop [\#93](https://github.com/tj-django/django-extra-field-validation/pull/93) ([jackton1](https://github.com/jackton1))
283 | - Feature/remove pinned package versions [\#92](https://github.com/tj-django/django-extra-field-validation/pull/92) ([jackton1](https://github.com/jackton1))
284 | - Add a Codacy badge to README.rst [\#91](https://github.com/tj-django/django-extra-field-validation/pull/91) ([codacy-badger](https://github.com/codacy-badger))
285 | - Update setup.py [\#89](https://github.com/tj-django/django-extra-field-validation/pull/89) ([jackton1](https://github.com/jackton1))
286 | - Update README.rst [\#88](https://github.com/tj-django/django-extra-field-validation/pull/88) ([jackton1](https://github.com/jackton1))
287 | - Update dependency mock to v4 [\#87](https://github.com/tj-django/django-extra-field-validation/pull/87) ([renovate[bot]](https://github.com/apps/renovate))
288 | - Update dependency yamllint to v1.24.2 [\#85](https://github.com/tj-django/django-extra-field-validation/pull/85) ([renovate[bot]](https://github.com/apps/renovate))
289 | - Update dependency tox to v3.20.0 [\#84](https://github.com/tj-django/django-extra-field-validation/pull/84) ([renovate[bot]](https://github.com/apps/renovate))
290 | - Update dependency six to v1.15.0 [\#26](https://github.com/tj-django/django-extra-field-validation/pull/26) ([renovate[bot]](https://github.com/apps/renovate))
291 | - Update dependency isort to v4.3.21 [\#24](https://github.com/tj-django/django-extra-field-validation/pull/24) ([renovate[bot]](https://github.com/apps/renovate))
292 | - Update dependency future to v0.18.2 [\#23](https://github.com/tj-django/django-extra-field-validation/pull/23) ([renovate[bot]](https://github.com/apps/renovate))
293 | - Update README.rst [\#18](https://github.com/tj-django/django-extra-field-validation/pull/18) ([jackton1](https://github.com/jackton1))
294 | - Configure Renovate [\#17](https://github.com/tj-django/django-extra-field-validation/pull/17) ([renovate[bot]](https://github.com/apps/renovate))
295 |
296 | ## [v0.1.13](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.13) (2020-02-23)
297 |
298 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.12...v0.1.13)
299 |
300 | ## [v0.1.12](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.12) (2020-02-23)
301 |
302 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.11...v0.1.12)
303 |
304 | **Closed issues:**
305 |
306 | - Update the `long_description_content_type` to text/x-rst [\#14](https://github.com/tj-django/django-extra-field-validation/issues/14)
307 |
308 | **Merged pull requests:**
309 |
310 | - Update README.rst [\#16](https://github.com/tj-django/django-extra-field-validation/pull/16) ([jackton1](https://github.com/jackton1))
311 |
312 | ## [v0.1.11](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.11) (2019-04-16)
313 |
314 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.8...v0.1.11)
315 |
316 | **Merged pull requests:**
317 |
318 | - Develop [\#13](https://github.com/tj-django/django-extra-field-validation/pull/13) ([jackton1](https://github.com/jackton1))
319 | - Update README.rst [\#11](https://github.com/tj-django/django-extra-field-validation/pull/11) ([jackton1](https://github.com/jackton1))
320 | - Fixed code style errors. [\#9](https://github.com/tj-django/django-extra-field-validation/pull/9) ([jackton1](https://github.com/jackton1))
321 |
322 | ## [v0.1.8](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.8) (2019-04-16)
323 |
324 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.10...v0.1.8)
325 |
326 | ## [v0.1.10](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.10) (2019-01-29)
327 |
328 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.9...v0.1.10)
329 |
330 | ## [v0.1.9](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.9) (2019-01-29)
331 |
332 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.7...v0.1.9)
333 |
334 | **Fixed bugs:**
335 |
336 | - No module named `dynamic_validator.field_validation` [\#7](https://github.com/tj-django/django-extra-field-validation/issues/7)
337 |
338 | **Merged pull requests:**
339 |
340 | - Updated the MANIFEST.in [\#8](https://github.com/tj-django/django-extra-field-validation/pull/8) ([jackton1](https://github.com/jackton1))
341 |
342 | ## [v0.1.7](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.7) (2019-01-29)
343 |
344 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.6...v0.1.7)
345 |
346 | ## [v0.1.6](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.6) (2019-01-29)
347 |
348 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.5...v0.1.6)
349 |
350 | ## [v0.1.5](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.5) (2019-01-27)
351 |
352 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.4...v0.1.5)
353 |
354 | **Merged pull requests:**
355 |
356 | - Removed redundant i.e statements [\#5](https://github.com/tj-django/django-extra-field-validation/pull/5) ([jackton1](https://github.com/jackton1))
357 | - Updated Django version. [\#4](https://github.com/tj-django/django-extra-field-validation/pull/4) ([jackton1](https://github.com/jackton1))
358 | - Update README.rst [\#2](https://github.com/tj-django/django-extra-field-validation/pull/2) ([jackton1](https://github.com/jackton1))
359 |
360 | ## [v0.1.4](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.4) (2018-12-09)
361 |
362 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.3...v0.1.4)
363 |
364 | ## [v0.1.3](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.3) (2018-12-09)
365 |
366 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.2...v0.1.3)
367 |
368 | ## [v0.1.2](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.2) (2018-12-09)
369 |
370 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.1...v0.1.2)
371 |
372 | ## [v0.1.1](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.1) (2018-12-09)
373 |
374 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.1.0...v0.1.1)
375 |
376 | ## [v0.1.0](https://github.com/tj-django/django-extra-field-validation/tree/v0.1.0) (2018-12-09)
377 |
378 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/v0.0.1...v0.1.0)
379 |
380 | ## [v0.0.1](https://github.com/tj-django/django-extra-field-validation/tree/v0.0.1) (2018-12-09)
381 |
382 | [Full Changelog](https://github.com/tj-django/django-extra-field-validation/compare/82382cb1beb5a4deaf24e444a7c541368394758c...v0.0.1)
383 |
384 |
385 |
386 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
387 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | jtonye@ymail.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Copyright 2018 Tonye Jack
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Tonye Jack
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | # added by check_manifest.py
2 | prune test*.py
3 | prune extra_validator/tests.py
4 | recursive-include extra_validator *.py
5 | include *.py
6 | exclude *.toml
7 | include *.txt
8 | exclude *.xml
9 | exclude .bumpversion.cfg
10 | exclude .coveragerc
11 | exclude Makefile
12 | exclude pytest.ini
13 | exclude tox.ini
14 | prune demo
15 | exclude manage.py
16 | exclude extra_validator/tests.py
17 | recursive-exclude django_extra_field_validation *.py
18 | include LICENSE-APACHE
19 | include LICENSE-MIT
20 | include README.rst
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Self-Documented Makefile see https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
2 |
3 | .DEFAULT_GOAL := help
4 |
5 | PYTHON := /usr/bin/env python
6 | MANAGE_PY := $(PYTHON) manage.py
7 | PYTHON_PIP := /usr/bin/env pip
8 | PIP_COMPILE := /usr/bin/env pip-compile
9 | PART := patch
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-32s-\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
14 |
15 | .PHONY: help
16 |
17 | guard-%: ## Checks that env var is set else exits with non 0 mainly used in CI;
18 | @if [ -z '${${*}}' ]; then echo 'Environment variable $* not set' && exit 1; fi
19 |
20 | # --------------------------------------------------------
21 | # ------- Python package (pip) management commands -------
22 | # --------------------------------------------------------
23 |
24 | clean-build: ## Clean project build artifacts.
25 | @echo "Removing build assets..."
26 | @$(PYTHON) setup.py clean
27 | @rm -rf build/
28 | @rm -rf dist/
29 | @rm -rf *.egg-info
30 |
31 | install: clean-build ## Install project dependencies.
32 | @echo "Installing project in dependencies..."
33 | @$(PYTHON_PIP) install -r requirements.txt
34 |
35 | install-lint: pipconf clean-build ## Install lint extra dependencies.
36 | @echo "Installing lint extra requirements..."
37 | @$(PYTHON_PIP) install -e .'[lint]'
38 |
39 | install-test: clean-build ## Install test extra dependencies.
40 | @echo "Installing test extra requirements..."
41 | @$(PYTHON_PIP) install -e .'[test]'
42 |
43 | install-dev: clean-build ## Install development extra dependencies.
44 | @echo "Installing development requirements..."
45 | @$(PYTHON_PIP) install -e .'[development]' -r requirements.txt
46 |
47 | install-deploy: clean-build ## Install deploy extra dependencies.
48 | @echo "Installing deploy extra requirements..."
49 | @$(PYTHON_PIP) install -e .'[deploy]'
50 |
51 | update-requirements: ## Updates the requirement.txt adding missing package dependencies
52 | @echo "Syncing the package requirements.txt..."
53 | @$(PIP_COMPILE)
54 |
55 | # ----------------------------------------------------------
56 | # ---------- Release the project to PyPI -------------------
57 | # ----------------------------------------------------------
58 | increase-version: guard-PART ## Increase project version
59 | @bump2version $(PART)
60 | @git switch -c main
61 |
62 | dist: ## builds source and wheel package
63 | @pip install build twine
64 | @python -m build
65 |
66 | release: dist ## package and upload a release
67 | @twine upload dist/*
68 |
69 | # ----------------------------------------------------------
70 | # --------- Run project Test -------------------------------
71 | # ----------------------------------------------------------
72 | test:
73 | @$(MANAGE_PY) test
74 |
75 | tox: install-test ## Run tox test
76 | @tox
77 |
78 | clean-test-all: clean-build ## Clean build and test assets.
79 | @rm -rf .tox/
80 | @rm -rf .pytest_cache/
81 | @rm test.db
82 |
83 |
84 | # -----------------------------------------------------------
85 | # --------- Docs ---------------------------------------
86 | # -----------------------------------------------------------
87 | create-docs:
88 | @npx docsify init ./docs
89 |
90 | serve-docs:
91 | @npx docsify serve ./docs
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # django-extra-field-validation
2 |
3 |    [](https://pepy.tech/project/django-extra-field-validation)
4 |
5 | [](https://github.com/tj-django/django-extra-field-validation/actions/workflows/test.yml)
6 | [](https://www.codacy.com/gh/tj-django/django-extra-field-validation/dashboard?utm_source=github.com\&utm_medium=referral\&utm_content=tj-django/django-extra-field-validation\&utm_campaign=Badge_Grade) [](https://www.codacy.com/gh/tj-django/django-extra-field-validation/dashboard?utm_source=github.com\&utm_medium=referral\&utm_content=tj-django/django-extra-field-validation\&utm_campaign=Badge_Coverage)
7 |
8 | ## Table of Contents
9 |
10 | * [Background](#background)
11 | * [Installation](#installation)
12 | * [Usage](#usage)
13 | * [Require all fields](#require-all-fields)
14 | * [Require at least one field in a collection](#require-at-least-one-field-in-a-collection)
15 | * [Optionally require at least one field in a collection](#optionally-require-at-least-one-field-in-a-collection)
16 | * [Conditionally require all fields](#conditionally-require-all-fields)
17 | * [Conditionally require at least one field in a collection](#conditionally-require-at-least-one-field-in-a-collection)
18 | * [Model Attributes](#model-attributes)
19 | * [License](#license)
20 | * [TODO's](#todos)
21 |
22 | ## Background
23 |
24 | This package aims to provide tools needed to define custom field validation logic which can be used independently or with
25 | django forms, test cases, API implementation or any model operation that requires saving data to the database.
26 |
27 | This can also be extended by defining check constraints if needed but currently validation
28 | will only be handled at the model level.
29 |
30 | ## Installation
31 |
32 | ```shell script
33 | pip install django-extra-field-validation
34 | ```
35 |
36 | ## Usage
37 |
38 | ### Require all fields
39 |
40 | ```py
41 |
42 | from django.db import models
43 | from extra_validator import FieldValidationMixin
44 |
45 |
46 | class TestModel(FieldValidationMixin, models.Model):
47 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
48 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
49 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
50 |
51 | REQUIRED_FIELDS = ['amount'] # Always requires an amount to create the instance.
52 | ```
53 |
54 | Example
55 |
56 | ```python
57 | In [1]: from decimal import Decimal
58 |
59 | In [2]: from demo.models import TestModel
60 |
61 | In [3]: TestModel.objects.create(fixed_price=Decimal('3.00'))
62 | ---------------------------------------------------------------------------
63 | ValueError Traceback (most recent call last)
64 | ...
65 |
66 | ValueError: {'amount': ValidationError([u'Please provide a value for: "amount".'])}
67 |
68 | ```
69 |
70 | ### Require at least one field in a collection
71 |
72 | ```py
73 |
74 | from django.db import models
75 | from extra_validator import FieldValidationMixin
76 |
77 |
78 | class TestModel(FieldValidationMixin, models.Model):
79 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
80 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
81 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
82 |
83 | REQUIRED_TOGGLE_FIELDS = [
84 | ['amount', 'fixed_price', 'percentage'], # Require only one of the following fields.
85 | ]
86 |
87 | ```
88 |
89 | Example
90 |
91 | ```python
92 | In [1]: from decimal import Decimal
93 |
94 | In [2]: from demo.models import TestModel
95 |
96 | In [3]: TestModel.objects.create(amount=Decimal('2.50'), fixed_price=Decimal('3.00'))
97 | ---------------------------------------------------------------------------
98 | ValueError Traceback (most recent call last)
99 | ...
100 |
101 | ValueError: {'fixed_price': ValidationError([u'Please provide only one of: Amount, Fixed price, Percentage'])}
102 |
103 | ```
104 |
105 | ### Optionally require at least one field in a collection
106 |
107 | ```py
108 |
109 | from django.db import models
110 | from extra_validator import FieldValidationMixin
111 |
112 |
113 | class TestModel(FieldValidationMixin, models.Model):
114 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
115 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
116 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
117 |
118 | OPTIONAL_TOGGLE_FIELDS = [
119 | ['fixed_price', 'percentage'] # Optionally validates that only fixed price/percentage are provided when present.
120 | ]
121 |
122 | ```
123 |
124 | Example
125 |
126 | ```python
127 | In [1]: from decimal import Decimal
128 |
129 | In [2]: from demo.models import TestModel
130 |
131 | In [3]: first_obj = TestModel.objects.create(amount=Decimal('2.0'))
132 |
133 | In [4]: second_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'))
134 |
135 | In [5]: third_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'), percentage=Decimal('10.0'))
136 | ---------------------------------------------------------------------------
137 | ValueError Traceback (most recent call last)
138 | ...
139 |
140 | ValueError: {'percentage': ValidationError([u'Please provide only one of: Fixed price, Percentage'])}
141 |
142 | ```
143 |
144 | ### Conditionally require all fields
145 |
146 | ```py
147 |
148 | from django.db import models
149 | from django.conf import settings
150 | from extra_validator import FieldValidationMixin
151 |
152 |
153 | class TestModel(FieldValidationMixin, models.Model):
154 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
155 |
156 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
157 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
158 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
159 |
160 | CONDITIONAL_REQUIRED_FIELDS = [
161 | (
162 | lambda instance: instance.user.is_active, ['amount', 'percentage'],
163 | ),
164 | ]
165 |
166 | ```
167 |
168 | Example
169 |
170 | ```python
171 | In [1]: from decimal import Decimal
172 |
173 | in [2]: from django.contrib.auth import get_user_model
174 |
175 | In [3]: from demo.models import TestModel
176 |
177 | In [4]: user = get_user_model().objects.create(username='test', is_active=True)
178 |
179 | In [5]: first_obj = TestModel.objects.create(user=user, amount=Decimal('2.0'))
180 | ---------------------------------------------------------------------------
181 | ValueError Traceback (most recent call last)
182 | ...
183 |
184 | ValueError: {u'percentage': ValidationError([u'Please provide a value for: "percentage"'])}
185 |
186 | ```
187 |
188 | ### Conditionally require at least one field in a collection
189 |
190 | ```py
191 |
192 | from django.db import models
193 | from django.conf import settings
194 | from extra_validator import FieldValidationMixin
195 |
196 |
197 | class TestModel(FieldValidationMixin, models.Model):
198 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
199 |
200 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
201 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
202 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
203 |
204 | CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [
205 | (
206 | lambda instance: instance.user.is_active, ['fixed_price', 'percentage', 'amount'],
207 | ),
208 | ]
209 | ```
210 |
211 | Example
212 |
213 | ```python
214 | In [1]: from decimal import Decimal
215 |
216 | in [2]: from django.contrib.auth import get_user_model
217 |
218 | In [3]: from demo.models import TestModel
219 |
220 | In [4]: user = get_user_model().objects.create(username='test', is_active=True)
221 |
222 | In [5]: first_obj = TestModel.objects.create(user=user)
223 | ---------------------------------------------------------------------------
224 | ValueError Traceback (most recent call last)
225 | ...
226 |
227 | ValueError: {'__all__': ValidationError([u'Please provide a valid value for any of the following fields: Fixed price, Percentage, Amount'])}
228 |
229 | In [6]: second_obj = TestModel.objects.create(user=user, amount=Decimal('2'), fixed_price=Decimal('2'))
230 | ---------------------------------------------------------------------------
231 | ValueError Traceback (most recent call last)
232 | ...
233 |
234 | ValueError: {'__all__': ValidationError([u'Please provide only one of the following fields: Fixed price, Percentage, Amount'])}
235 | ```
236 |
237 | ## Model Attributes
238 |
239 | This is done using model attributes below.
240 |
241 | ```py
242 | # A list of required fields
243 | REQUIRED_FIELDS = []
244 |
245 | # A list of fields with at most one required.
246 | REQUIRED_TOGGLE_FIELDS = []
247 |
248 | # A list of field with at least one required.
249 | REQUIRED_MIN_FIELDS = []
250 |
251 | # Optional list of fields with at most one required.
252 | OPTIONAL_TOGGLE_FIELDS = []
253 |
254 | # Conditional field required list of tuples the condition a boolean or a callable.
255 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Both 'first_name' or 'last_name'
256 | # If condition is True ensure that all fields are set
257 | CONDITIONAL_REQUIRED_FIELDS = []
258 |
259 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Either 'first_name' or 'last_name'
260 | # If condition is True ensure that at most one field is set
261 | CONDITIONAL_REQUIRED_TOGGLE_FIELDS = []
262 |
263 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : At least 'first_name' or 'last_name' provided or both
264 | # If condition is True ensure that at least one field is set
265 | CONDITIONAL_REQUIRED_MIN_FIELDS = []
266 |
267 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Both 'first_name' and 'last_name' isn't provided
268 | # If condition is True ensure none of the fields are provided
269 | CONDITIONAL_REQUIRED_EMPTY_FIELDS = []
270 |
271 | ```
272 |
273 | ## License
274 |
275 | django-extra-field-validation is distributed under the terms of both
276 |
277 | * [MIT License](https://choosealicense.com/licenses/mit)
278 | * [Apache License, Version 2.0](https://choosealicense.com/licenses/apache-2.0)
279 |
280 | at your option.
281 |
282 | ## TODO's
283 |
284 | * \[ ] Support `CONDITIONAL_NON_REQUIRED_TOGGLE_FIELDS`
285 | * \[ ] Support `CONDITIONAL_NON_REQUIRED_FIELDS`
286 | * \[ ] Move to support class and function based validators that use the instance object this should enable cross field model validation.
287 |
--------------------------------------------------------------------------------
/demo/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tj-django/django-extra-field-validation/e15a1177fc416945548655c227ff5811a9329b68/demo/__init__.py
--------------------------------------------------------------------------------
/demo/apps.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from django.apps import AppConfig
3 |
4 |
5 | class DemoConfig(AppConfig):
6 | name = "demo"
7 |
--------------------------------------------------------------------------------
/demo/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.17 on 2018-12-08 23:41
3 |
4 | import django.db.models.deletion
5 | from django.conf import settings
6 | from django.db import migrations, models
7 |
8 | import extra_validator.field_validation.validator
9 |
10 |
11 | class Migration(migrations.Migration):
12 | initial = True
13 |
14 | dependencies = [
15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name="TestModel",
21 | fields=[
22 | (
23 | "id",
24 | models.AutoField(
25 | auto_created=True,
26 | primary_key=True,
27 | serialize=False,
28 | verbose_name="ID",
29 | ),
30 | ),
31 | (
32 | "amount",
33 | models.DecimalField(
34 | blank=True, decimal_places=2, max_digits=5, null=True
35 | ),
36 | ),
37 | (
38 | "fixed_price",
39 | models.DecimalField(
40 | blank=True, decimal_places=2, max_digits=7, null=True
41 | ),
42 | ),
43 | (
44 | "percentage",
45 | models.DecimalField(
46 | blank=True, decimal_places=0, max_digits=3, null=True
47 | ),
48 | ),
49 | (
50 | "user",
51 | models.ForeignKey(
52 | on_delete=django.db.models.deletion.CASCADE,
53 | to=settings.AUTH_USER_MODEL,
54 | ),
55 | ),
56 | ],
57 | bases=(
58 | extra_validator.field_validation.validator.FieldValidationMixin,
59 | models.Model,
60 | ),
61 | ),
62 | ]
63 |
--------------------------------------------------------------------------------
/demo/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tj-django/django-extra-field-validation/e15a1177fc416945548655c227ff5811a9329b68/demo/migrations/__init__.py
--------------------------------------------------------------------------------
/demo/models.py:
--------------------------------------------------------------------------------
1 | import django
2 | from django.contrib.auth import get_user_model
3 | from django.db import models
4 |
5 | from extra_validator import FieldValidationMixin
6 |
7 | if django.VERSION <= (3, 0):
8 | from django.utils.translation import ugettext_noop as _
9 | else:
10 | from django.utils.translation import gettext_noop as _
11 |
12 | UserModel = get_user_model()
13 |
14 |
15 | class TestModel(FieldValidationMixin, models.Model):
16 | """Ensure that at least one of the following fields are provided."""
17 |
18 | user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
19 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
20 | fixed_price = models.DecimalField(
21 | max_digits=7, decimal_places=2, null=True, blank=True
22 | )
23 | percentage = models.DecimalField(
24 | max_digits=3, decimal_places=0, null=True, blank=True
25 | )
26 |
27 | def __str__(self):
28 | return _(
29 | "{0}: (#{1}, {2}%, ${3})".format(
30 | self.__class__.__name__, self.amount, self.percentage, self.fixed_price
31 | )
32 | )
33 |
--------------------------------------------------------------------------------
/django_extra_field_validation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tj-django/django-extra-field-validation/e15a1177fc416945548655c227ff5811a9329b68/django_extra_field_validation/__init__.py
--------------------------------------------------------------------------------
/django_extra_field_validation/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for django_extra_field_validation project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.0.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.0/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.0/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = "8vb(_&-!#z=hk_7k6j5u6qvu_yz2fr_oisvev+yybt@$@_$4bk"
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | "django.contrib.admin",
35 | "django.contrib.auth",
36 | "django.contrib.contenttypes",
37 | "django.contrib.sessions",
38 | "django.contrib.messages",
39 | "django.contrib.staticfiles",
40 | "extra_validator",
41 | "demo",
42 | ]
43 |
44 | MIDDLEWARE = [
45 | "django.middleware.security.SecurityMiddleware",
46 | "django.contrib.sessions.middleware.SessionMiddleware",
47 | "django.middleware.common.CommonMiddleware",
48 | "django.middleware.csrf.CsrfViewMiddleware",
49 | "django.contrib.auth.middleware.AuthenticationMiddleware",
50 | "django.contrib.messages.middleware.MessageMiddleware",
51 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
52 | ]
53 |
54 | TEMPLATES = [
55 | {
56 | "BACKEND": "django.template.backends.django.DjangoTemplates",
57 | "DIRS": [],
58 | "APP_DIRS": True,
59 | "OPTIONS": {
60 | "context_processors": [
61 | "django.template.context_processors.debug",
62 | "django.template.context_processors.request",
63 | "django.contrib.auth.context_processors.auth",
64 | "django.contrib.messages.context_processors.messages",
65 | ],
66 | },
67 | },
68 | ]
69 |
70 | WSGI_APPLICATION = "django_extra_field_validation.wsgi.application"
71 |
72 |
73 | # Database
74 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases
75 |
76 | DATABASES = {
77 | "default": {
78 | "ENGINE": "django.db.backends.sqlite3",
79 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
80 | }
81 | }
82 |
83 |
84 | # Password validation
85 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
86 |
87 | AUTH_PASSWORD_VALIDATORS = [
88 | {
89 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
90 | },
91 | {
92 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
93 | },
94 | {
95 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
96 | },
97 | {
98 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
99 | },
100 | ]
101 |
102 |
103 | # Internationalization
104 | # https://docs.djangoproject.com/en/2.0/topics/i18n/
105 |
106 | TIME_ZONE = "UTC"
107 |
108 | USE_I18N = True
109 |
110 | USE_L10N = True
111 |
112 | USE_TZ = True
113 |
114 |
115 | # Static files (CSS, JavaScript, Images)
116 | # https://docs.djangoproject.com/en/2.0/howto/static-files/
117 |
118 | STATIC_URL = "/static/"
119 |
120 | SECURE_HSTS_SECONDS = 3600
121 |
122 | SECURE_HSTS_INCLUDE_SUBDOMAINS = True
123 |
124 | SECURE_CONTENT_TYPE_NOSNIFF = True
125 |
126 | SECURE_SSL_REDIRECT = os.getenv("SECURE_SSL_REDIRECT_ENABLED") != "False"
127 |
128 | SESSION_COOKIE_SECURE = os.getenv("SESSION_COOKIE_SECURE_ENABLED") != "False"
129 |
130 | CSRF_COOKIE_SECURE = os.getenv("CSRF_COOKIE_SECURE_ENABLED") != "False"
131 |
132 | SECURE_HSTS_PRELOAD = True
133 |
--------------------------------------------------------------------------------
/django_extra_field_validation/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for django_extra_field_validation project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault(
15 | "DJANGO_SETTINGS_MODULE", "django_extra_field_validation.settings"
16 | )
17 |
18 | application = get_wsgi_application()
19 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tj-django/django-extra-field-validation/e15a1177fc416945548655c227ff5811a9329b68/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # django-extra-field-validation
2 |
3 |    [](https://pepy.tech/project/django-extra-field-validation)
4 |
5 | [](https://github.com/tj-django/django-extra-field-validation/actions/workflows/test.yml)
6 | [](https://www.codacy.com/gh/tj-django/django-extra-field-validation/dashboard?utm_source=github.com\&utm_medium=referral\&utm_content=tj-django/django-extra-field-validation\&utm_campaign=Badge_Grade) [](https://www.codacy.com/gh/tj-django/django-extra-field-validation/dashboard?utm_source=github.com\&utm_medium=referral\&utm_content=tj-django/django-extra-field-validation\&utm_campaign=Badge_Coverage)
7 |
8 | ## Table of Contents
9 |
10 | * [Background](#background)
11 | * [Installation](#installation)
12 | * [Usage](#usage)
13 | * [Require all fields](#require-all-fields)
14 | * [Require at least one field in a collection](#require-at-least-one-field-in-a-collection)
15 | * [Optionally require at least one field in a collection](#optionally-require-at-least-one-field-in-a-collection)
16 | * [Conditionally require all fields](#conditionally-require-all-fields)
17 | * [Conditionally require at least one field in a collection](#conditionally-require-at-least-one-field-in-a-collection)
18 | * [Model Attributes](#model-attributes)
19 | * [License](#license)
20 | * [TODO's](#todos)
21 |
22 | ## Background
23 |
24 | This package aims to provide tools needed to define custom field validation logic which can be used independently or with
25 | django forms, test cases, API implementation or any model operation that requires saving data to the database.
26 |
27 | This can also be extended by defining check constraints if needed but currently validation
28 | will only be handled at the model level.
29 |
30 | ## Installation
31 |
32 | ```shell script
33 | pip install django-extra-field-validation
34 | ```
35 |
36 | ## Usage
37 |
38 | ### Require all fields
39 |
40 | ```py
41 |
42 | from django.db import models
43 | from extra_validator import FieldValidationMixin
44 |
45 |
46 | class TestModel(FieldValidationMixin, models.Model):
47 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
48 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
49 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
50 |
51 | REQUIRED_FIELDS = ['amount'] # Always requires an amount to create the instance.
52 | ```
53 |
54 | Example
55 |
56 | ```python
57 | In [1]: from decimal import Decimal
58 |
59 | In [2]: from demo.models import TestModel
60 |
61 | In [3]: TestModel.objects.create(fixed_price=Decimal('3.00'))
62 | ---------------------------------------------------------------------------
63 | ValueError Traceback (most recent call last)
64 | ...
65 |
66 | ValueError: {'amount': ValidationError([u'Please provide a value for: "amount".'])}
67 |
68 | ```
69 |
70 | ### Require at least one field in a collection
71 |
72 | ```py
73 |
74 | from django.db import models
75 | from extra_validator import FieldValidationMixin
76 |
77 |
78 | class TestModel(FieldValidationMixin, models.Model):
79 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
80 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
81 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
82 |
83 | REQUIRED_TOGGLE_FIELDS = [
84 | ['amount', 'fixed_price', 'percentage'], # Require only one of the following fields.
85 | ]
86 |
87 | ```
88 |
89 | Example
90 |
91 | ```python
92 | In [1]: from decimal import Decimal
93 |
94 | In [2]: from demo.models import TestModel
95 |
96 | In [3]: TestModel.objects.create(amount=Decimal('2.50'), fixed_price=Decimal('3.00'))
97 | ---------------------------------------------------------------------------
98 | ValueError Traceback (most recent call last)
99 | ...
100 |
101 | ValueError: {'fixed_price': ValidationError([u'Please provide only one of: Amount, Fixed price, Percentage'])}
102 |
103 | ```
104 |
105 | ### Optionally require at least one field in a collection
106 |
107 | ```py
108 |
109 | from django.db import models
110 | from extra_validator import FieldValidationMixin
111 |
112 |
113 | class TestModel(FieldValidationMixin, models.Model):
114 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
115 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
116 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
117 |
118 | OPTIONAL_TOGGLE_FIELDS = [
119 | ['fixed_price', 'percentage'] # Optionally validates that only fixed price/percentage are provided when present.
120 | ]
121 |
122 | ```
123 |
124 | Example
125 |
126 | ```python
127 | In [1]: from decimal import Decimal
128 |
129 | In [2]: from demo.models import TestModel
130 |
131 | In [3]: first_obj = TestModel.objects.create(amount=Decimal('2.0'))
132 |
133 | In [4]: second_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'))
134 |
135 | In [5]: third_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'), percentage=Decimal('10.0'))
136 | ---------------------------------------------------------------------------
137 | ValueError Traceback (most recent call last)
138 | ...
139 |
140 | ValueError: {'percentage': ValidationError([u'Please provide only one of: Fixed price, Percentage'])}
141 |
142 | ```
143 |
144 | ### Conditionally require all fields
145 |
146 | ```py
147 |
148 | from django.db import models
149 | from django.conf import settings
150 | from extra_validator import FieldValidationMixin
151 |
152 |
153 | class TestModel(FieldValidationMixin, models.Model):
154 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
155 |
156 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
157 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
158 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
159 |
160 | CONDITIONAL_REQUIRED_FIELDS = [
161 | (
162 | lambda instance: instance.user.is_active, ['amount', 'percentage'],
163 | ),
164 | ]
165 |
166 | ```
167 |
168 | Example
169 |
170 | ```python
171 | In [1]: from decimal import Decimal
172 |
173 | in [2]: from django.contrib.auth import get_user_model
174 |
175 | In [3]: from demo.models import TestModel
176 |
177 | In [4]: user = get_user_model().objects.create(username='test', is_active=True)
178 |
179 | In [5]: first_obj = TestModel.objects.create(user=user, amount=Decimal('2.0'))
180 | ---------------------------------------------------------------------------
181 | ValueError Traceback (most recent call last)
182 | ...
183 |
184 | ValueError: {u'percentage': ValidationError([u'Please provide a value for: "percentage"'])}
185 |
186 | ```
187 |
188 | ### Conditionally require at least one field in a collection
189 |
190 | ```py
191 |
192 | from django.db import models
193 | from django.conf import settings
194 | from extra_validator import FieldValidationMixin
195 |
196 |
197 | class TestModel(FieldValidationMixin, models.Model):
198 | user = models.ForeignKey(settings.AUTH_USER_MODEL)
199 |
200 | amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
201 | fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
202 | percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
203 |
204 | CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [
205 | (
206 | lambda instance: instance.user.is_active, ['fixed_price', 'percentage', 'amount'],
207 | ),
208 | ]
209 | ```
210 |
211 | Example
212 |
213 | ```python
214 | In [1]: from decimal import Decimal
215 |
216 | in [2]: from django.contrib.auth import get_user_model
217 |
218 | In [3]: from demo.models import TestModel
219 |
220 | In [4]: user = get_user_model().objects.create(username='test', is_active=True)
221 |
222 | In [5]: first_obj = TestModel.objects.create(user=user)
223 | ---------------------------------------------------------------------------
224 | ValueError Traceback (most recent call last)
225 | ...
226 |
227 | ValueError: {'__all__': ValidationError([u'Please provide a valid value for any of the following fields: Fixed price, Percentage, Amount'])}
228 |
229 | In [6]: second_obj = TestModel.objects.create(user=user, amount=Decimal('2'), fixed_price=Decimal('2'))
230 | ---------------------------------------------------------------------------
231 | ValueError Traceback (most recent call last)
232 | ...
233 |
234 | ValueError: {'__all__': ValidationError([u'Please provide only one of the following fields: Fixed price, Percentage, Amount'])}
235 | ```
236 |
237 | ## Model Attributes
238 |
239 | This is done using model attributes below.
240 |
241 | ```py
242 | # A list of required fields
243 | REQUIRED_FIELDS = []
244 |
245 | # A list of fields with at most one required.
246 | REQUIRED_TOGGLE_FIELDS = []
247 |
248 | # A list of field with at least one required.
249 | REQUIRED_MIN_FIELDS = []
250 |
251 | # Optional list of fields with at most one required.
252 | OPTIONAL_TOGGLE_FIELDS = []
253 |
254 | # Conditional field required list of tuples the condition a boolean or a callable.
255 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Both 'first_name' or 'last_name'
256 | # If condition is True ensure that all fields are set
257 | CONDITIONAL_REQUIRED_FIELDS = []
258 |
259 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Either 'first_name' or 'last_name'
260 | # If condition is True ensure that at most one field is set
261 | CONDITIONAL_REQUIRED_TOGGLE_FIELDS = []
262 |
263 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : At least 'first_name' or 'last_name' provided or both
264 | # If condition is True ensure that at least one field is set
265 | CONDITIONAL_REQUIRED_MIN_FIELDS = []
266 |
267 | # [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Both 'first_name' and 'last_name' isn't provided
268 | # If condition is True ensure none of the fields are provided
269 | CONDITIONAL_REQUIRED_EMPTY_FIELDS = []
270 |
271 | ```
272 |
273 | ## License
274 |
275 | django-extra-field-validation is distributed under the terms of both
276 |
277 | * [MIT License](https://choosealicense.com/licenses/mit)
278 | * [Apache License, Version 2.0](https://choosealicense.com/licenses/apache-2.0)
279 |
280 | at your option.
281 |
282 | ## TODO's
283 |
284 | * \[ ] Support `CONDITIONAL_NON_REQUIRED_TOGGLE_FIELDS`
285 | * \[ ] Support `CONDITIONAL_NON_REQUIRED_FIELDS`
286 | * \[ ] Move to support class and function based validators that use the instance object this should enable cross field model validation.
287 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Please wait...
14 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/extra_validator/__init__.py:
--------------------------------------------------------------------------------
1 | """Top-level package for django-extra-field-validation."""
2 |
3 | __author__ = """Tonye Jack"""
4 | __email__ = "jtonye@ymail.com"
5 | __version__ = "1.2.3"
6 |
7 | from .field_validation import FieldValidationMixin
8 |
9 | __all__ = ["FieldValidationMixin"]
10 |
--------------------------------------------------------------------------------
/extra_validator/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class DynamicValidatorConfig(AppConfig):
5 | name = "extra_validator"
6 |
--------------------------------------------------------------------------------
/extra_validator/field_validation/__init__.py:
--------------------------------------------------------------------------------
1 | from .validator import FieldValidationMixin # noqa
2 |
--------------------------------------------------------------------------------
/extra_validator/field_validation/validator.py:
--------------------------------------------------------------------------------
1 | import django
2 | from django.core import validators
3 | from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
4 | from django.db import models
5 |
6 | if django.VERSION <= (3, 0):
7 | from django.utils.translation import ugettext as _
8 | else:
9 | from django.utils.translation import gettext as _
10 |
11 |
12 | def field_to_str(fields):
13 | return _(
14 | ", ".join(
15 | map(lambda fname: fname.replace("_", " ").strip().capitalize(), fields)
16 | ),
17 | )
18 |
19 |
20 | class FieldValidationMixin(object):
21 | # A list of required fields
22 | REQUIRED_FIELDS = []
23 | # A list of fields with at most one required.
24 | REQUIRED_TOGGLE_FIELDS = []
25 | # A list of field with at least one required.
26 | REQUIRED_MIN_FIELDS = []
27 | # Optional list of fields with at most one required.
28 | OPTIONAL_TOGGLE_FIELDS = []
29 |
30 | # Conditional field required list of tuples the condition a boolean or a callable.
31 | # (lambda o: o.offer_type != Offer.OfferType.OTHER.value, ['offer_detail_id', 'content_type'])
32 | # If condition is True ensure that all fields are set
33 | CONDITIONAL_REQUIRED_FIELDS = []
34 | # If condition is True ensure that at most one field is set
35 | CONDITIONAL_REQUIRED_TOGGLE_FIELDS = []
36 | # If condition is True ensure that at least one field is set
37 | CONDITIONAL_REQUIRED_MIN_FIELDS = []
38 | # If condition is True ensure none of the fields are provided
39 | CONDITIONAL_REQUIRED_EMPTY_FIELDS = []
40 |
41 | ERROR_HANDLERS = {"form": ValidationError}
42 |
43 | EMPTY_VALUES = list(validators.EMPTY_VALUES)
44 |
45 | @staticmethod
46 | def _error_as_dict(field, msg, code="required", error_class=ValidationError):
47 | return {field: error_class(_(msg), code=code)}
48 |
49 | def _validate_only_one_option(self, selections, found_keys):
50 | error_dict = {}
51 | for section in selections:
52 | provided_fields = []
53 | for key in found_keys:
54 | if key in section:
55 | provided_fields.append(key)
56 | if len(set(provided_fields)) > 1:
57 | msg = "Please provide only one of: {fields}".format(
58 | fields=field_to_str(section)
59 | )
60 | error_dict.update(
61 | self._error_as_dict(provided_fields[-1], msg, code="invalid")
62 | )
63 | break
64 | return error_dict
65 |
66 | def _clean_conditional_toggle_fields(self, exclude=None, context=None):
67 | if self.CONDITIONAL_REQUIRED_TOGGLE_FIELDS:
68 | return self._clean_conditional_fields(
69 | exclude,
70 | context,
71 | field_sets=self.CONDITIONAL_REQUIRED_TOGGLE_FIELDS,
72 | validate_one=True,
73 | )
74 |
75 | def _clean_conditional_min_fields(self, exclude=None, context=None):
76 | if self.CONDITIONAL_REQUIRED_MIN_FIELDS:
77 | return self._clean_conditional_fields(
78 | exclude,
79 | context,
80 | field_sets=self.CONDITIONAL_REQUIRED_MIN_FIELDS,
81 | at_least_one=True,
82 | )
83 |
84 | def _clean_conditional_empty_fields(self, exclude=None, context=None):
85 | if self.CONDITIONAL_REQUIRED_EMPTY_FIELDS:
86 | return self._clean_conditional_fields(
87 | exclude,
88 | context,
89 | field_sets=self.CONDITIONAL_REQUIRED_EMPTY_FIELDS,
90 | none_provided=True,
91 | )
92 |
93 | def _clean_conditional_fields(
94 | self,
95 | exclude=None,
96 | context=None,
97 | field_sets=(),
98 | validate_one=False,
99 | at_least_one=False,
100 | none_provided=False,
101 | ):
102 | error_class = self.ERROR_HANDLERS.get(context, ValueError)
103 | exclude = exclude or []
104 | errors = {}
105 | field_sets = field_sets or self.CONDITIONAL_REQUIRED_FIELDS
106 |
107 | if all([field_sets, isinstance(field_sets, (list, tuple))]):
108 | for condition, fields in field_sets:
109 | field_names = list(filter(lambda f: f not in exclude, fields))
110 | if field_names:
111 | is_valid_condition = (
112 | condition if isinstance(condition, bool) else False
113 | )
114 | if callable(condition):
115 | is_valid_condition = condition(self)
116 |
117 | field_value_map = {
118 | field_name: getattr(self, field_name)
119 | for field_name in field_names
120 | if getattr(self, field_name) not in self.EMPTY_VALUES
121 | }
122 |
123 | if is_valid_condition:
124 | if not field_value_map and not none_provided:
125 | if len(fields) > 1:
126 | msg = (
127 | "Please provide a valid value for the following fields: "
128 | "{fields}"
129 | if not validate_one
130 | else "Please provide a valid value for any of the following "
131 | "fields: {fields}".format(
132 | fields=field_to_str(fields)
133 | )
134 | )
135 | errors.update(
136 | self._error_as_dict(NON_FIELD_ERRORS, msg)
137 | )
138 | else:
139 | field = fields[0]
140 | msg = 'Please provide a value for: "{field}"'.format(
141 | field=field
142 | )
143 | errors.update(self._error_as_dict(field, msg))
144 | break
145 |
146 | if field_value_map and none_provided:
147 | msg = "Please omit changes to the following fields: {fields}".format(
148 | fields=field_to_str(fields)
149 | )
150 | errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
151 | break
152 |
153 | missing_fields = [
154 | field_name
155 | for field_name in fields
156 | if field_name not in field_value_map.keys()
157 | ]
158 |
159 | if not validate_one and not at_least_one and not none_provided:
160 | for missing_field in missing_fields:
161 | msg = 'Please provide a value for: "{missing_field}"'.format(
162 | missing_field=missing_field
163 | )
164 | errors.update(self._error_as_dict(missing_field, msg))
165 |
166 | elif validate_one and len(fields) - 1 != len(
167 | list(missing_fields)
168 | ):
169 | msg = (
170 | "Please provide only one of the following fields: "
171 | "{fields}".format(fields=field_to_str(fields))
172 | )
173 | errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
174 |
175 | if errors:
176 | raise error_class(errors)
177 |
178 | def _clean_required_and_optional_fields(self, exclude=None, context=None):
179 | """Provide extra validation for fields that are required and single selection fields."""
180 | exclude = exclude or []
181 | if any(
182 | [
183 | self.REQUIRED_TOGGLE_FIELDS,
184 | self.REQUIRED_MIN_FIELDS,
185 | self.REQUIRED_FIELDS,
186 | self.OPTIONAL_TOGGLE_FIELDS,
187 | ]
188 | ):
189 | error_class = self.ERROR_HANDLERS.get(context, ValueError)
190 | found = []
191 | errors = {}
192 | optional = []
193 |
194 | for f in self._meta.fields:
195 | if f.name not in exclude:
196 | raw_value = getattr(self, f.attname)
197 | if f.name in self.REQUIRED_FIELDS:
198 | if raw_value in f.empty_values and not f.has_default():
199 | msg = 'Please provide a value for: "{field_name}".'.format(
200 | field_name=f.name
201 | )
202 | errors.update(self._error_as_dict(f.name, msg))
203 |
204 | for required_min_field in self.REQUIRED_MIN_FIELDS:
205 | # Multiple selection of at least one required.
206 | if f.name in required_min_field:
207 | if raw_value not in f.empty_values:
208 | found.append({f.name: raw_value})
209 | elif raw_value in f.empty_values and f.has_default():
210 | if (
211 | isinstance(f, models.CharField)
212 | and f.get_default() not in f.empty_values
213 | ):
214 | found.append({f.name: f.get_default()})
215 |
216 | for required_toggle_field in self.REQUIRED_TOGGLE_FIELDS:
217 | # Single selection of at most one required.
218 | if f.name in required_toggle_field:
219 | if raw_value not in f.empty_values:
220 | found.append({f.name: raw_value})
221 | elif raw_value in f.empty_values and f.has_default():
222 | if (
223 | isinstance(f, models.CharField)
224 | and f.get_default() not in f.empty_values
225 | ):
226 | found.append({f.name: f.get_default()})
227 |
228 | for optional_toggle_field in self.OPTIONAL_TOGGLE_FIELDS:
229 | if (
230 | f.name in optional_toggle_field
231 | and raw_value not in f.empty_values
232 | ):
233 | optional.append({f.name: raw_value})
234 |
235 | if self.REQUIRED_MIN_FIELDS:
236 | if not found:
237 | fields_str = "\n, ".join(
238 | [field_to_str(fields) for fields in self.REQUIRED_MIN_FIELDS]
239 | )
240 | fields_str = (
241 | "\n {fields}".format(fields=fields_str)
242 | if len(self.REQUIRED_MIN_FIELDS) > 1
243 | else fields_str
244 | )
245 | msg = "Please provide a valid value for any of the following fields: {fields}".format(
246 | fields=fields_str
247 | )
248 | errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
249 |
250 | if self.REQUIRED_TOGGLE_FIELDS:
251 | if not found:
252 | fields_str = "\n, ".join(
253 | [field_to_str(fields) for fields in self.REQUIRED_TOGGLE_FIELDS]
254 | )
255 | fields_str = (
256 | "\n {fields}".format(fields=fields_str)
257 | if len(self.REQUIRED_TOGGLE_FIELDS) > 1
258 | else fields_str
259 | )
260 | msg = "Please provide a valid value for any of the following fields: {fields}".format(
261 | fields=fields_str
262 | )
263 | errors.update(self._error_as_dict(NON_FIELD_ERRORS, msg))
264 | else:
265 | found_keys = [k for item in found for k in item.keys()]
266 | errors.update(
267 | self._validate_only_one_option(
268 | self.REQUIRED_TOGGLE_FIELDS, found_keys
269 | ),
270 | )
271 |
272 | if self.OPTIONAL_TOGGLE_FIELDS:
273 | if optional:
274 | optional_keys = [k for item in optional for k in item.keys()]
275 | errors.update(
276 | self._validate_only_one_option(
277 | self.OPTIONAL_TOGGLE_FIELDS, optional_keys
278 | ),
279 | )
280 |
281 | if errors:
282 | raise error_class(errors)
283 |
284 | def clean_fields(self, exclude=None):
285 | self._clean_conditional_toggle_fields(exclude=exclude, context="form")
286 | self._clean_conditional_min_fields(exclude=exclude, context="form")
287 | self._clean_conditional_empty_fields(exclude=exclude, context="form")
288 | self._clean_conditional_fields(exclude=exclude, context="form")
289 | self._clean_required_and_optional_fields(exclude=exclude, context="form")
290 | return super(FieldValidationMixin, self).clean_fields(exclude=exclude)
291 |
292 | def save(self, *args, **kwargs):
293 | self._clean_conditional_toggle_fields()
294 | self._clean_conditional_min_fields()
295 | self._clean_conditional_empty_fields()
296 | self._clean_conditional_fields()
297 | self._clean_required_and_optional_fields()
298 | return super(FieldValidationMixin, self).save(*args, **kwargs)
299 |
--------------------------------------------------------------------------------
/extra_validator/tests.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import get_user_model
2 | from django.test import TestCase
3 |
4 | User = get_user_model()
5 |
6 |
7 | class FieldValidationTestCase(TestCase):
8 | @classmethod
9 | def setUpTestData(cls):
10 | cls.super_user = User.objects.create(
11 | username="super-test-user", is_superuser=True
12 | )
13 | cls.user = User.objects.create(username="test-user")
14 |
15 | def test_conditional_required_field_raises_exception_when_missing(self):
16 | from demo.models import TestModel
17 |
18 | TestModel.CONDITIONAL_REQUIRED_FIELDS = [
19 | (
20 | lambda instance: instance.user.is_active,
21 | ["percentage"],
22 | ),
23 | ]
24 |
25 | with self.assertRaises(ValueError):
26 | TestModel.objects.create(user=self.user)
27 |
28 | def test_conditional_required_field_is_valid(self):
29 | from demo.models import TestModel
30 |
31 | TestModel.CONDITIONAL_REQUIRED_FIELDS = [
32 | (
33 | lambda instance: instance.user.is_active,
34 | ["percentage"],
35 | ),
36 | ]
37 |
38 | TestModel.objects.create(user=self.user, percentage=25)
39 |
40 | def test_conditional_required_toggle_field_raises_exception_when_missing(self):
41 | from demo.models import TestModel
42 |
43 | TestModel.CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [
44 | (
45 | lambda instance: instance.user.is_active,
46 | ["fixed_price", "percentage", "amount"],
47 | ),
48 | ]
49 |
50 | with self.assertRaises(ValueError):
51 | TestModel.objects.create(user=self.user)
52 |
53 | def test_conditional_required_toggle_field_raises_exception_with_2_fields(
54 | self,
55 | ):
56 | from demo.models import TestModel
57 |
58 | TestModel.CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [
59 | (
60 | lambda instance: instance.user.is_active,
61 | ["fixed_price", "percentage", "amount"],
62 | ),
63 | ]
64 |
65 | with self.assertRaises(ValueError):
66 | TestModel.objects.create(user=self.user, percentage=25, fixed_price=10)
67 |
68 | def test_conditional_required_toggle_field_is_valid(self):
69 | from demo.models import TestModel
70 |
71 | TestModel.CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [
72 | (
73 | lambda instance: instance.user.is_active,
74 | ["fixed_price", "percentage", "amount"],
75 | ),
76 | ]
77 |
78 | TestModel.objects.create(user=self.user, percentage=25)
79 |
80 | def test_required_fields_raises_exception(self):
81 | from demo.models import TestModel
82 |
83 | TestModel.REQUIRED_FIELDS = ["percentage"]
84 |
85 | with self.assertRaises(ValueError):
86 | TestModel.objects.create(user=self.user)
87 |
88 | def test_providing_a_required_field_saves_the_instance(self):
89 | from demo.models import TestModel
90 |
91 | TestModel.REQUIRED_FIELDS = ["percentage"]
92 |
93 | obj = TestModel.objects.create(user=self.user, percentage=25)
94 |
95 | self.assertEqual(obj.percentage, 25)
96 |
97 | def test_providing_more_than_one_required_field_raises_an_error(self):
98 | from demo.models import TestModel
99 |
100 | TestModel.REQUIRED_FIELDS = ["percentage", "fixed_price"]
101 |
102 | with self.assertRaises(ValueError):
103 | TestModel.objects.create(user=self.user, percentage=25, fixed_price=10)
104 |
105 | def test_optional_required_fields_is_valid(self):
106 | from demo.models import TestModel
107 |
108 | TestModel.REQUIRED_TOGGLE_FIELDS = ["fixed_price", "percentage", "amount"]
109 |
110 | TestModel.objects.create(user=self.user, percentage=25)
111 |
112 | def test_optional_required_fields_raised_exception_when_invalid(self):
113 | from demo.models import TestModel
114 |
115 | TestModel.REQUIRED_TOGGLE_FIELDS = ["fixed_price", "percentage", "amount"]
116 |
117 | with self.assertRaises(ValueError):
118 | TestModel.objects.create(user=self.user)
119 | TestModel.objects.create(user=self.user, percentage=25, amount=25)
120 | TestModel.objects.create(user=self.user, fixed_price=25, amount=25)
121 |
122 | def test_optional_toggle_fields_is_valid(self):
123 | from demo.models import TestModel
124 |
125 | TestModel.OPTIONAL_TOGGLE_FIELDS = ["fixed_price", "percentage", "amount"]
126 |
127 | TestModel.objects.create(user=self.user, percentage=25)
128 |
129 | def test_optional_toggle_fields_raised_exception_when_invalid(self):
130 | from demo.models import TestModel
131 |
132 | TestModel.OPTIONAL_TOGGLE_FIELDS = ["fixed_price", "percentage", "amount"]
133 |
134 | with self.assertRaises(ValueError):
135 | TestModel.objects.create(user=self.user, percentage=25, amount=25)
136 | TestModel.objects.create(user=self.user, fixed_price=25, amount=25)
137 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault(
7 | "DJANGO_SETTINGS_MODULE", "django_extra_field_validation.settings"
8 | )
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | )
17 | execute_from_command_line(sys.argv)
18 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = 'django-extra-field-validation'
3 | version = '0.0.1'
4 | description = ''
5 | author = 'Tonye Jack'
6 | author_email = 'jtonye@ymail.com'
7 | license = 'MIT/Apache-2.0'
8 | url = 'https://github.com/tj-django/django-extra-field-validation.git'
9 |
10 | [requires]
11 | python_version = ['2.7', '3.5', '3.6', 'pypy', 'pypy3']
12 |
13 | [build-system]
14 | requires = ['setuptools', 'wheel']
15 |
16 | [tool.hatch.commands]
17 | prerelease = 'hatch build'
18 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | DJANGO_SETTINGS_MODULE = django_extra_field_validation.settings
3 | python_files = tests.py test_*.py *_tests.py
4 | testpaths = extra_validator demo
5 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "enabled": true,
6 | "prHourlyLimit": 10,
7 | "prConcurrentLimit": 5,
8 | "rebaseWhen": "behind-base-branch",
9 | "addLabels": [
10 | "dependencies"
11 | ],
12 | "assignees": [
13 | "jackton1"
14 | ],
15 | "assignAutomerge": true,
16 | "dependencyDashboard": true,
17 | "dependencyDashboardAutoclose": true,
18 | "lockFileMaintenance": {
19 | "enabled": true,
20 | "automerge": true
21 | },
22 | "packageRules": [
23 | {
24 | "matchUpdateTypes": ["major", "minor", "patch", "pin", "digest"],
25 | "automerge": true,
26 | "rebaseWhen": "behind-base-branch",
27 | "addLabels": [
28 | "automerge"
29 | ]
30 | },
31 | {
32 | "description": "docker images",
33 | "matchLanguages": [
34 | "docker"
35 | ],
36 | "matchUpdateTypes": ["major", "minor", "patch", "pin", "digest"],
37 | "rebaseWhen": "behind-base-branch",
38 | "addLabels": [
39 | "automerge"
40 | ],
41 | "automerge": true
42 | }
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile with Python 3.9
3 | # by the following command:
4 | #
5 | # pip-compile
6 | #
7 | asgiref==3.8.1
8 | # via django
9 | django==5.1.6
10 | # via django-extra-field-validation (setup.py)
11 | sqlparse==0.5.3
12 | # via django
13 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import io
2 | import os
3 |
4 | from setuptools import find_packages, setup
5 |
6 | install_requires = ["Django"]
7 |
8 | test_requires = [
9 | "tox",
10 | "pytest-django",
11 | "pluggy>=0.7",
12 | "mock",
13 | "codacy-coverage",
14 | ]
15 |
16 | deploy_requires = [
17 | "readme_renderer[md]",
18 | "bump2version==1.0.1",
19 | ]
20 |
21 | lint_requires = [
22 | "flake8",
23 | "yamllint",
24 | "isort",
25 | ]
26 |
27 | local_dev_requires = [
28 | "pip-tools",
29 | "check-manifest",
30 | ]
31 |
32 | extras_require = {
33 | "development": [
34 | local_dev_requires,
35 | install_requires,
36 | test_requires,
37 | lint_requires,
38 | ],
39 | "test": test_requires,
40 | "lint": lint_requires,
41 | "deploy": deploy_requires,
42 | "tox": local_dev_requires,
43 | }
44 |
45 | BASE_DIR = os.path.dirname(__file__)
46 | README_PATH = os.path.join(BASE_DIR, "README.md")
47 |
48 | if os.path.isfile(README_PATH):
49 | with io.open(README_PATH, encoding="utf-8") as f:
50 | LONG_DESCRIPTION = f.read()
51 | else:
52 | LONG_DESCRIPTION = ""
53 |
54 |
55 | setup(
56 | name="django-extra-field-validation",
57 | version="1.2.3",
58 | description="Extra django field validation.",
59 | python_requires=">=2.6",
60 | long_description=LONG_DESCRIPTION,
61 | long_description_content_type="text/markdown",
62 | author="Tonye Jack",
63 | author_email="jtonye@ymail.com",
64 | maintainer="Tonye Jack",
65 | maintainer_email="jtonye@ymail.com",
66 | url="https://github.com/tj-django/django-extra-field-validation.git",
67 | license="MIT/Apache-2.0",
68 | keywords=[
69 | "django",
70 | "model validation",
71 | "django models",
72 | "django object validation",
73 | "field validation",
74 | "conditional validation",
75 | "cross field validation",
76 | "django validation",
77 | "django validators",
78 | "django custom validation",
79 | ],
80 | classifiers=[
81 | "Development Status :: 5 - Production/Stable",
82 | "Intended Audience :: Developers",
83 | "License :: OSI Approved :: MIT License",
84 | "License :: OSI Approved :: Apache Software License",
85 | "Natural Language :: English",
86 | "Topic :: Internet :: WWW/HTTP",
87 | "Operating System :: OS Independent",
88 | "Programming Language :: Python :: 3.6",
89 | "Programming Language :: Python :: 3.7",
90 | "Programming Language :: Python :: 3.8",
91 | "Programming Language :: Python :: 3.9",
92 | "Programming Language :: Python :: Implementation :: CPython",
93 | "Programming Language :: Python :: Implementation :: PyPy",
94 | "Framework :: Django :: 2.0",
95 | "Framework :: Django :: 2.1",
96 | "Framework :: Django :: 2.2",
97 | "Framework :: Django :: 3.0",
98 | "Framework :: Django :: 3.1",
99 | ],
100 | install_requires=install_requires,
101 | tests_require=["coverage", "pytest"],
102 | extras_require=extras_require,
103 | packages=find_packages(
104 | exclude=["*.tests", "*.tests.*", "tests.*", "tests*", "demo", "manage.*"]
105 | ),
106 | )
107 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | minversion = 3.1.2
3 | skip_missing_interpreters = true
4 |
5 | envlist =
6 | py36-django{20,21,22,30,31,32,main}-{linux,macos,windows}
7 | py37-django{20,21,22,30,31,32,main}-{linux,macos,windows}
8 | py38-django{21,22,30,31,32,40,41,main}-{linux,macos,windows}
9 | py39-django{21,22,30,31,32,40,41,main}-{linux,macos,windows}
10 | py310-django{22,30,31,32,40,41,main}-{linux,macos,windows}
11 | py311-django{22,30,31,32,40,41,main}-{linux}
12 |
13 | [gh-actions]
14 | python =
15 | 3.6: py36
16 | 3.7: py37
17 | 3.8: py38
18 | 3.9: py39
19 | 3.10: py310
20 | 3.11: py311
21 |
22 | [gh-actions:env]
23 | PLATFORM =
24 | ubuntu-latest: linux
25 | macos-latest: macos
26 | windows-latest: windows
27 |
28 | [testenv]
29 | extras = tox
30 | passenv = *
31 | deps =
32 | django20: Django>=2.0,<2.1
33 | django21: Django>=2.1,<2.2
34 | django22: Django>=2.2,<2.3
35 | django30: Django>=3.0,<3.1
36 | django31: Django>=3.1,<3.2
37 | django32: Django>=3.2,<3.3
38 | django40: Django>=4.0,<4.1
39 | django41: Django>=4.1,<4.2
40 | main: https://github.com/django/django/archive/main.tar.gz
41 | coverage
42 | pytest-django
43 | codacy-coverage
44 | usedevelop = False
45 | commands =
46 | python -c "import django; print(django.VERSION)"
47 | coverage run -m pytest
48 | coverage report -m
49 | coverage xml
50 | - python-codacy-coverage -r coverage.xml
51 |
52 |
53 | [flake8]
54 | max-line-length = 120
55 |
--------------------------------------------------------------------------------