pax_global_header00006660000000000000000000000064145011561570014517gustar00rootroot0000000000000052 comment=eb4424798bb6ba60c3eab5ca1a23f86891051516 django-dynamic-fixture-4.0.1/000077500000000000000000000000001450115615700160715ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/.coveragerc000066400000000000000000000005361450115615700202160ustar00rootroot00000000000000[run] source = django_dynamic_fixture omit = */migrations/* [report] exclude_lines = pragma: no cover def __repr__ if self.debug: if settings.DEBUG raise AssertionError raise NotImplementedError if 0: if __name__ == .__main__.: def __iter__ def __len__ __metaclass__ def __init__ class MetaChoice django-dynamic-fixture-4.0.1/.flake8000066400000000000000000000001671450115615700172500ustar00rootroot00000000000000[flake8] max-line-length=200 exclude=.git,*migrations/*,tmp/*,scripts/*,docs/*,*/tests/*,proto,env/* max-complexity=15 django-dynamic-fixture-4.0.1/.github/000077500000000000000000000000001450115615700174315ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/.github/workflows/000077500000000000000000000000001450115615700214665ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/.github/workflows/release.yml000066400000000000000000000052531450115615700236360ustar00rootroot00000000000000name: Building and Testing on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: 'ubuntu-latest' strategy: matrix: python_version: - '3.8' - '3.9' - '3.10' - '3.11' django_version: - '4.0' - '4.1' - '4.2' services: postgres: # https://github.com/postgis/docker-postgis/blob/master/15-3.4/alpine/Dockerfile image: postgis/postgis:15-3.4-alpine env: POSTGRES_DB: ddf POSTGRES_USER: ddf_user POSTGRES_PASSWORD: ddf_pass ports: # Random free port - 5432/tcp # Set health checks to wait until postgres has started options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: 'Install OS dependencies (Ubuntu)' if: runner.os == 'Linux' run: | # https://docs.djangoproject.com/en/4.2/ref/contrib/gis/install/geolibs/ # GDAL_LIBRARY_PATH: /usr/lib/libgdal.so.* # GEOS_LIBRARY_PATH: /usr/lib/libgeos_c.so.* # ln -s /usr/lib/libgdal.so.32 /usr/lib/libgdal.so \ # ln -s /usr/lib/libgeos_c.so.1 /usr/lib/libgeos_c.so # ln -s /usr/lib/libproj.so.25 /usr/lib/libproj.so \ sudo apt-get update \ && sudo apt-get install -y binutils libproj-dev gdal-bin - name: 'Set up Python ${{ matrix.python_version }}' uses: actions/setup-python@v4 with: python-version: ${{ matrix.python_version }} - name: 'Checkout DDF code' uses: actions/checkout@v3 - name: 'Install Python dependencies' run: | pip install -r requirements.txt pip install -r requirements-dev.txt pip install pytest-django pip install django~=${{ matrix.django_version }} pip install jsonfield==3.1.0 pip install django-polymorphic==3.1.0 - name: 'Testing with SQLite' run: pytest --create-db --reuse-db --no-migrations --ds=settings_sqlite --maxfail=2 - name: 'Coverage with SQLite' run: pytest --create-db --reuse-db --no-migrations -v --cov=django_dynamic_fixture - name: 'Testing with Postgres' env: POSTGRES_HOST: localhost POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} POSTGRES_DB: ddf POSTGRES_USER: ddf_user POSTGRES_PASSWORD: ddf_pass run: | pip install psycopg2 pytest --create-db --reuse-db --no-migrations --ds=settings_postgres --maxfail=2 django-dynamic-fixture-4.0.1/.gitignore000066400000000000000000000007521450115615700200650ustar00rootroot00000000000000.DS_Store .idea/ dist/ build/ cover/ .settings .project .pydevproject .settings/org.eclipse.core.resources.prefs .settings/org.eclipse.core.runtime.prefs .settings/org.eclipse.ltk.core.refactoring.prefs .settings/org.eclipse.wst.validation.prefs *.bat *.sh *.coverage *.pyc *.pyo *.log .noseids *.sqlite virtualenv* django_dynamic_fixture.egg-info env/ envpypy env26 env27 env32 env33 env34 env2.6 env2.7 env3.2 env3.3 env3.4 .tox test_:memory: .eggs/ ddf_compatibility_report.* htmlcov/*django-dynamic-fixture-4.0.1/.pylintrc000066400000000000000000000427131450115615700177450ustar00rootroot00000000000000[MASTER] # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. extension-pkg-whitelist= # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the # number of processors available to use. jobs=1 # Control the amount of potential inferred values when inferring a single # object. This can help the performance when dealing with large functions or # complex, nested conditions. limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes # Specify a configuration file. #rcfile= # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. confidence= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once). You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=print-statement, parameter-unpacking, unpacking-in-except, old-raise-syntax, backtick, long-suffix, old-ne-operator, old-octal-literal, import-star-module-level, non-ascii-bytes-literal, raw-checker-failed, bad-inline-option, locally-disabled, file-ignored, suppressed-message, useless-suppression, deprecated-pragma, use-symbolic-message-instead, apply-builtin, basestring-builtin, buffer-builtin, cmp-builtin, coerce-builtin, execfile-builtin, file-builtin, long-builtin, raw_input-builtin, reduce-builtin, standarderror-builtin, unicode-builtin, xrange-builtin, coerce-method, delslice-method, getslice-method, setslice-method, no-absolute-import, old-division, dict-iter-method, dict-view-method, next-method-called, metaclass-assignment, indexing-exception, raising-string, reload-builtin, oct-method, hex-method, nonzero-method, cmp-method, input-builtin, round-builtin, intern-builtin, unichr-builtin, map-builtin-not-iterating, zip-builtin-not-iterating, range-builtin-not-iterating, filter-builtin-not-iterating, using-cmp-argument, eq-without-hash, div-method, idiv-method, rdiv-method, exception-message-attribute, invalid-str-codec, sys-max-int, bad-python3-import, deprecated-string-function, deprecated-str-translate-call, deprecated-itertools-function, deprecated-types-field, next-method-defined, dict-items-not-iterating, dict-keys-not-iterating, dict-values-not-iterating, deprecated-operator-function, deprecated-urllib-function, xreadlines-attribute, deprecated-sys-function, exception-escape, comprehension-escape # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [REPORTS] # Python expression which should return a score less than or equal to 10. You # have access to the variables 'error', 'warning', 'refactor', and 'convention' # which contain the number of messages in each category, as well as 'statement' # which is the total number of statements analyzed. This score is used by the # global evaluation report (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details. #msg-template= # Set the output format. Available formats are text, parseable, colorized, json # and msvs (visual studio). You can also give a reporter class, e.g. # mypackage.mymodule.MyReporterClass. output-format=text # Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. score=yes [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=5 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=sys.exit [LOGGING] # Format style used to check logging format string. `old` means using % # formatting, `new` is for `{}` formatting,and `fstr` is for f-strings. logging-format-style=old # Logging modules to check that the string format arguments are in logging # function parameter format. logging-modules=logging [SPELLING] # Limits count of emitted suggestions for spelling mistakes. max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: none. To make it work, # install the python-enchant package. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains the private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to the private dictionary (see the # --spelling-private-dict-file option) instead of raising a message. spelling-store-unknown-words=no [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # Tells whether to warn about missing members when the owner of the attribute # is inferred to be None. ignore-none=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis). It # supports qualified module names, as well as Unix pattern matching. ignored-modules= # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 # List of decorators that change the signature of a decorated function. signature-mutators= [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid defining new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expected to # not be used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. Default to name # with leading underscore. ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=past.builtins,future.builtins,builtins,io [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Maximum number of characters on a single line. max-line-length=200 # Maximum number of lines in a module. max-module-lines=1000 # List of optional constructs for which whitespace checking is disabled. `dict- # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. # `trailing-comma` allows a space between comma and closing bracket: (a, ). # `empty-line` allows space-only lines. no-space-check=trailing-comma, dict-separator # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [SIMILARITIES] # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no # Minimum lines number of a similarity. min-similarity-lines=4 [BASIC] # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style. #argument-rgx= # Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style. #attr-rgx= # Bad variable names which should always be refused, separated by a comma. bad-names=foo, bar, baz, toto, tutu, tata # Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style. #class-attribute-rgx= # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming- # style. #class-rgx= # Naming style matching correct constant names. const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style. #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names. function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style. #function-rgx= # Good variable names which should always be accepted, separated by a comma. good-names=i, j, k, ex, Run, _ # Include a hint for the correct naming format with invalid-name. include-naming-hint=no # Naming style matching correct inline iteration names. inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style. #inlinevar-rgx= # Naming style matching correct method names. method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style. #method-rgx= # Naming style matching correct module names. module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style. #module-rgx= # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. # These decorators are taken in consideration only for invalid-name. property-classes=abc.abstractproperty # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style. #variable-rgx= [STRING] # This flag controls whether the implicit-str-concat-in-sequence should # generate a warning on implicit string concatenation in sequences defined over # several lines. check-str-concat-over-line-jumps=no [IMPORTS] # List of modules that can be imported at any level, not just the top level # one. allow-any-import-level= # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Deprecated modules which should not be used, separated by a comma. deprecated-modules=optparse,tkinter.tix # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled). ext-import-graph= # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled). import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled). int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant # Couples of modules and preferred modules, separated by a comma. preferred-modules= [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp, __post_init__ # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=cls [DESIGN] # Maximum number of arguments for function / method. max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 # Maximum number of branch for function / method body. max-branches=12 # Maximum number of locals for function / method body. max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of return / yield for function / method body. max-returns=6 # Maximum number of statements in function / method body. max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". overgeneral-exceptions=BaseException, Exception django-dynamic-fixture-4.0.1/.python-version000066400000000000000000000000441450115615700210740ustar00rootroot000000000000003.7.17 3.8.17 3.9.17 3.10.12 3.11.4 django-dynamic-fixture-4.0.1/.readthedocs.yaml000066400000000000000000000002541450115615700213210ustar00rootroot00000000000000# https://docs.readthedocs.io/en/stable/config-file/v2.html version: 2 build: os: ubuntu-22.04 tools: python: "3.11" sphinx: configuration: docs/source/conf.py django-dynamic-fixture-4.0.1/LICENSE.txt000066400000000000000000000032521450115615700177160ustar00rootroot00000000000000MIT license: http://opensource.org/licenses/MIT Copyright (c) <2013> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Apache 2.0 Copyright [2013] [Paulo Cheque] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.django-dynamic-fixture-4.0.1/MANIFEST.in000066400000000000000000000000001450115615700176150ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/Makefile000066400000000000000000000075161450115615700175420ustar00rootroot00000000000000VERSION=4.0.1 # Python env tasks clean: clear @find . -type f -name "*.py[co]" -delete @find . -type d -name "__pycache__" -delete rm -rf *.egg rm -rf *.egg-info/ rm -rf *.log rm -rf ~* rm -rf data/ rm -rf dist/ rm -rf build/ rm -rf .eggs/ rm -rf .tox/ #rm -rf env/ os_deps: brew install gdal prepare: clear ; python3.11 -m venv env deps: clear env/bin/python -m pip install --upgrade pip env/bin/python -m pip install --upgrade setuptools wheel env/bin/pip install -r requirements.txt env/bin/pip install -r requirements-dev.txt env/bin/pip list shell: #clear ; env/bin/python -i -c "from ddf import *" clear ; env/bin/python manage.py shell # from ddf import * # Python code tasks compile: env/bin/python -OO -m compileall . test: # Run specific test: # TESTS=django_dynamic_fixture.tests.FILE::CLASS::METHOD make test clear ; time env/bin/pytest --create-db --reuse-db --no-migrations ${TESTS} testfailed: clear ; env/bin/pytest --create-db --reuse-db --no-migrations ${TESTS} --last-failed config_postgres: psql -U postgres -c "create extension postgis" # set up postgresql psql -U postgres -c "create role cacheops login superuser" # postgis django backend requires these to exist psql -U postgres -c "create database cacheops" psql -U postgres -c "create database cacheops_slave" # create db and user psql -c "CREATE DATABASE ddf;" -U postgres psql -c "CREATE USER ddf_user WITH PASSWORD 'ddf_pass';" -U postgres psql -c "ALTER USER ddf_user CREATEDB;" -U postgres psql -c "ALTER USER ddf_user WITH SUPERUSER;" -U postgres test_postgres: # TESTS=django_dynamic_fixture.tests.FILE::CLASS::METHOD make test_postgres clear ; env/bin/pytest --reuse-db --no-migrations --ds=settings_postgres ${TESTS} test_mysql: # TESTS=django_dynamic_fixture.tests.FILE::CLASS::METHOD make test_mysql clear ; env/bin/pytest --reuse-db --no-migrations --ds=settings_mysql ${TESTS} cov: clear ; env/bin/pytest --create-db --reuse-db --no-migrations -v --cov=django_dynamic_fixture --cov-report html cp htmlcov/index.html docs/source/_static/coverage.html open htmlcov/index.html code_style: # Code Style clear ; env/bin/pylint ddf django_dynamic_fixture queries code_checking: # Code error checking clear ; env/bin/python -m pyflakes ddf django_dynamic_fixture queries code_feedbacks: # PEP8, code style and circular complexity clear ; env/bin/flake8 ddf django_dynamic_fixture queries code_ruff: clear ; env/bin/ruff check . #clear ; env/bin/ruff check . --fix check: code_style code_checking code_feedbacks code_ruff install_precommit_hooks: code_ruff clear ; env/bin/ruff check . env/bin/pre-commit install doc: cov clear ; cd docs ; make clean html ; open build/html/index.html tox: #brew update ; brew install pyenv #pyenv install 3.8 3.9 3.10 3.11 #pyenv versions #pyenv local 3.7 3.8 3.9 3.10 3.11 clear ; time env/bin/tox --parallel all build: clean os_deps prepare deps test cov # Python package tasks lib: clean test cov doc # clear ; env/bin/python setup.py build # clear ; env/bin/python setup.py sdist clear ; env/bin/python -m build clear ; env/bin/twine check dist/* publish: lib # Fixing Python 3 Certificates # /Applications/Python\ 3.7/Install\ Certificates.command # Manual upload to PypI # http://pypi.python.org/pypi/THE-PROJECT # Go to 'edit' link # Update version and save # Go to 'files' link and upload the file clear ; env/bin/twine upload dist/* --username=UPDATE_ME --password=UPDATE_ME # Git tasks push: tox doc clear ; git push origin `git symbolic-ref --short HEAD` tag: git tag ${VERSION} git push origin ${VERSION} reset_tag: git tag -d ${VERSION} git push origin :refs/tags/${VERSION} # GitHub Action act: #brew install act time act --container-architecture linux/amd64 --matrix python_version:3.11 --matrix django_version:4.2 actall: time act --container-architecture linux/amd64 django-dynamic-fixture-4.0.1/README.md000066400000000000000000000072131450115615700173530ustar00rootroot00000000000000Django Dynamic Fixture ====================== [![Docs Status](https://readthedocs.org/projects/django-dynamic-fixture/badge/?version=latest)](http://django-dynamic-fixture.readthedocs.org/en/latest/index.html) [![PyPI version](https://badge.fury.io/py/django-dynamic-fixture.svg)](https://badge.fury.io/py/django-dynamic-fixture) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-dynamic-fixture) ![PyPI - Downloads](https://img.shields.io/pypi/dm/django-dynamic-fixture) **Latest version: 4.0.1 (Sep 2023)** Django Dynamic Fixture (DDF) is a complete and simple library to create dynamic model instances for testing purposes. It lets you focus on your tests, instead of focusing on generating some dummy data which is boring and polutes the test source code. * [Basic Examples](#basic-examples) * [Cheat Sheet](#cheat-sheet) * Full Documentation Basic Examples -------------- > Customize only the important details of the test: ```python from ddf import G from my_library import Author, Book def test_search_book_by_author(): author1 = G(Author) author2 = G(Author) book1 = G(Book, authors=[author1]) book2 = G(Book, authors=[author2]) books = Book.objects.search_by_author(author1.name) assert book1 in books assert book2 not in books ``` > Using some goodies to keep the test code smaller: ```python from ddf import G def test_search_book_by_author(): author1, author2 = G('my_library.Author', n=2) book1 = G('my_library.Book', authors=[author1]) book2 = G('my_library.Book', authors=[author2]) books = Book.objects.search_by_author(author1.name) assert book1 in books assert book2 not in books ``` > Configuring data from relationship fields: ```python from ddf import G def test_search_book_by_author(): book1 = G(Book, main_author__name='Eistein') book2 = G(Book) books = Book.objects.search_by_author(book1.main_author.name) assert book1 in books assert book2 not in books assert book1.main_author.name == 'Eistein' ``` Cheat Sheet -------------- ```python # Import the main DDF features from ddf import N, G, F, M, C, P, teach # meaning: New, Get, ForeignKey, Mask, Copier, Print, teach ``` ```python # `N` creates an instance of model without saving it to DB instance = N(Book) ``` ```python # `G` creates an instance of model and save it into the DB instance = G(Book) ``` ```python # `F` customize relationship objects instance = G(Book, author=F(name='Eistein')) # Same as `F` instance = G(Book, author__name='Eistein') ``` ```python # `M` receives a data mask and create a random string using it # Known symbols: `_`, `#` or `-` # To escape known symbols: `!` instance = N(Book, address=M('Street ___, ### !- --')) assert instance.address == 'Street TPA, 632 - BR' ``` ```python # `C` copies data from one field to another instance = N(Book, address_formatted=C('address'), address=M('Street ___, ### \- --')) assert instance.address_formatted == 'Street TPA, 632 - BR' ``` ```python # `teach` teaches DDF in how to build an instance teach(Book, address=M('Street ___, ### !- --')) instance = G(Book) assert instance.address == 'Street TPA, 632 - BR' ``` ```python # `P` print instance values for debugging P(instance) ``` ```python import ddf ddf.__version__ ``` ```python from ddf import ddf_check_models succeeded, errors = ddf_check_models() succeeded, errors = ddf_check_models(print_csv=True) succeeded, errors = ddf_check_models(csv_filename='ddf_compatibility_report.csv') ``` django-dynamic-fixture-4.0.1/ddf/000077500000000000000000000000001450115615700166265ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/ddf/__init__.py000066400000000000000000000007331450115615700207420ustar00rootroot00000000000000# Short alias to use: # `from ddf import *` instead of `from django_dynamic_fixture import *` from django_dynamic_fixture import N, G, F, C, M, P, PRE_SAVE, POST_SAVE, __version__ from django_dynamic_fixture import new, get, fixture, teach, look_up_alias from django_dynamic_fixture.decorators import skip_for_database, only_for_database from django_dynamic_fixture.fdf import FileSystemDjangoTestCase from django_dynamic_fixture.script_ddf_checkings import ddf_check_models django-dynamic-fixture-4.0.1/django_dynamic_fixture/000077500000000000000000000000001450115615700226055ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/__init__.py000066400000000000000000000176361450115615700247330ustar00rootroot00000000000000 """ This is the facade of all features of DDF. Module that contains wrappers and shortcuts (aliases). """ import warnings from django.apps import apps from django_dynamic_fixture.ddf import DynamicFixture, Copier, Mask, DDFLibrary, \ set_pre_save_receiver, set_post_save_receiver from django_dynamic_fixture.django_helper import print_field_values, django_greater_than from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, \ StaticSequentialDataFixture from django_dynamic_fixture.global_settings import DDF_DEFAULT_DATA_FIXTURE, DDF_FILL_NULLABLE_FIELDS, DDF_FK_MIN_DEPTH, \ DDF_IGNORE_FIELDS, DDF_VALIDATE_MODELS, \ DDF_DEBUG_MODE, DDF_FIELD_FIXTURES from django_dynamic_fixture.script_ddf_checkings import ddf_check_models __version__ = '4.0.1' if not django_greater_than(4, 0): warnings.warn("DDF 4.* officially supports only Django 4 or higher.", DeprecationWarning) LOOKUP_SEP = '__' def look_up_alias(ddf_as_f=True, **kwargs): """ Example of parameters: a__b__c=1 => {a: {b: {c: 1}}} """ field_dict = {} params_to_be_skipped = [] for alias, value in kwargs.items(): parts = alias.split(LOOKUP_SEP) if len(parts) == 1: params_to_be_skipped.append(alias) level_dict = field_dict for part in parts[:-1]: level_dict = level_dict.setdefault(part, {}) level_dict[parts[-1]] = value if ddf_as_f: for root, value in field_dict.items(): if root in params_to_be_skipped: field_dict[root] = value else: field_dict[root] = dict_to_f(value) return field_dict def dict_to_f(value): """ Example: 1 => 1 {b: 1} => F(b=1) {b: {c: 1}} => F(b=F(c=1)) """ if not isinstance(value, dict): return value else: kwargs = {} for k, v in value.items(): if not isinstance(v, dict): kwargs[k] = v else: kwargs[k] = dict_to_f(v) return F(**kwargs) def fixture(**kwargs): """ DynamicFixture factory: It instantiate a DynamicFixture using global configurations. Same as F(...) """ kwargs = look_up_alias(**kwargs) f = DynamicFixture(data_fixture=kwargs.pop('data_fixture', DDF_DEFAULT_DATA_FIXTURE), fill_nullable_fields=kwargs.pop('fill_nullable_fields', DDF_FILL_NULLABLE_FIELDS), ignore_fields=kwargs.pop('ignore_fields', []), fk_min_depth=kwargs.pop('fk_min_depth', DDF_FK_MIN_DEPTH), validate_models=kwargs.pop('validate_models', DDF_VALIDATE_MODELS), print_errors=kwargs.pop('print_errors', True), debug_mode=kwargs.pop('debug_mode', DDF_DEBUG_MODE), **kwargs) return f # Wrappers def _new(model, n=1, ddf_lesson=None, persist_dependencies=False, **kwargs): """ Return one or many valid instances of Django Models with fields filled with auto generated or customized data. All instances will NOT be persisted in the database, except its dependencies, in case @persist_dependencies is True. @model: The class of the Django model. It can be a string `.` @n: number of instances to be created with the given configuration. Default is 1. @ddf_lesson: use a custom ddf_lesson to build the model object. @persist_dependencies: If True, save internal dependencies, otherwise just instantiate them. Default is False. @data_fixture: override DDF_DEFAULT_DATA_FIXTURE configuration. Default is SequentialDataFixture(). @fill_nullable_fields: override DDF_FILL_NULLABLE_FIELDS global configuration. Default is True. @ignore_fields: List of fields that will be ignored by DDF. It will be concatenated with the global list DDF_IGNORE_FIELDS. Default is []. @fk_min_depth: override DDF_FK_MIN_DEPTH global configuration. Default 0. @validate_models: override DDF_VALIDATE_MODELS global configuration. Default is False. @print_errors: print on console all instance values if DDF can not generate a valid object with the given configuration. Wrapper for the method DynamicFixture.new """ if isinstance(model, str): model = apps.get_model(model) kwargs = look_up_alias(**kwargs) d = fixture(**kwargs) if n == 1: return d.new(model, ddf_lesson=ddf_lesson, persist_dependencies=persist_dependencies, **kwargs) instances = [] for _ in range(n): instances.append(d.new(model, ddf_lesson=ddf_lesson, persist_dependencies=persist_dependencies, **kwargs)) return instances def _get(model, n=1, ddf_lesson=None, **kwargs): """ Return one or many valid instances of Django Models with fields filled with auto generated or customized data. All instances will be persisted in the database. @model: The class of the Django model. It can be a string `.` @n: number of instances to be created with the given configuration. Default is 1. @ddf_lesson: use a custom ddf_lesson to build the model object. @data_fixture: override DDF_DEFAULT_DATA_FIXTURE configuration. Default is SequentialDataFixture(). @fill_nullable_fields: override DDF_FILL_NULLABLE_FIELDS global configuration. Default is True. @ignore_fields: List of fields that will be ignored by DDF. It will be concatenated with the global list DDF_IGNORE_FIELDS. Default is []. @fk_min_depth: override DDF_FK_MIN_DEPTH global configuration. Default 0. @validate_models: override DDF_VALIDATE_MODELS global configuration. Default is False. @print_errors: print on console all instance values if DDF can not generate a valid object with the given configuration. Wrapper for the method DynamicFixture.get """ if isinstance(model, str): model = apps.get_model(model) kwargs = look_up_alias(**kwargs) d = fixture(**kwargs) if n == 1: return d.get(model, ddf_lesson=ddf_lesson, **kwargs) instances = [] for _ in range(n): instances.append(d.get(model, ddf_lesson=ddf_lesson, **kwargs)) return instances def _teach(model, ddf_lesson=None, **kwargs): ''' @model: The class of the Django model. It can be a string `.` @ddf_lesson: Name of custom ddf_lesson to be created. @raise an CantOverrideddf_lesson error if the same model/ddf_lesson were called twice. Sometimes DDF can't create an model instance because the particularities of the model. The workaround for this is to teach DDF how to create it. Basically, we use the same object creation approach that will be saved as a template for the next DDF calls. Use this method to teach DDF how to create an instance. New metaphor for the shelve/library feature. `Shelve` becomes `Teach` `Library` becomes `Lessons` ''' if isinstance(model, str): model = apps.get_model(model) kwargs = look_up_alias(**kwargs) d = fixture(**kwargs) return d.teach(model, ddf_lesson=ddf_lesson, **kwargs) # Shortcuts N = new = _new G = get = _get T = teach = _teach F = fixture C = Copier M = Mask P = print_field_values DDFLibrary = DDFLibrary PRE_SAVE = set_pre_save_receiver POST_SAVE = set_post_save_receiver import typing INSTANCE_TYPE = typing.TypeVar('INSTANCE') def new(model: typing.Type[INSTANCE_TYPE], n: int = 1, ddf_lesson = None, persist_dependencies: bool = True, **kwargs) -> INSTANCE_TYPE: return _new(model, n=n, ddf_lesson=ddf_lesson, persist_dependencies=persist_dependencies, **kwargs) def get(model: typing.Type[INSTANCE_TYPE], n: int=1, ddf_lesson=None, **kwargs) -> INSTANCE_TYPE: return _get(model, n=n, ddf_lesson=ddf_lesson, **kwargs) def teach(model: typing.Type[INSTANCE_TYPE], ddf_lesson=None, **kwargs): return _teach(model, ddf_lesson=ddf_lesson, **kwargs) N = new G = get T = teach django-dynamic-fixture-4.0.1/django_dynamic_fixture/ddf.py000066400000000000000000000732031450115615700237210ustar00rootroot00000000000000 import inspect import logging import re import sys from django.core.files import File from django.db.models import Field try: from importlib import import_module except ImportError: from django.utils.importlib import import_module from django_dynamic_fixture.django_helper import get_related_model, \ field_has_choices, field_has_default_value, get_fields_from_model, \ print_field_values, get_many_to_many_fields_from_model, \ get_unique_model_name, get_unique_field_name, is_model_abstract, \ field_is_a_parent_link, get_field_by_name_or_raise, get_app_name_of_model, \ is_model_class, is_relationship_field, is_file_field, is_key_field, \ model_has_the_field, enable_auto_now, disable_auto_now, enable_auto_now_add, disable_auto_now_add LOGGER = logging.getLogger('DDFLog') _PRE_SAVE = {} # receivers to be executed before saving a instance; _POST_SAVE = {} # receivers to be executed after saving a instance; class UnsupportedFieldError(Exception): "DynamicFixture does not support this field." class InvalidCopierExpressionError(Exception): "The specified expression used in a Copier is invalid." class InvalidConfigurationError(Exception): "The specified configuration for the field can not be applied or it is bugged." class InvalidManyToManyConfigurationError(Exception): "M2M attribute configuration must be a number or a list of DynamicFixture or model instances." class BadDataError(Exception): "The data passed to a field has some problem (not unique or invalid) or a required attribute is in ignore list." class InvalidModelError(Exception): "Invalid Model: The class is not a model or it is abstract." class InvalidReceiverError(Exception): "Receiver is not a function that receive only the instance as parameter." class PendingField(Exception): "Internal exception to control pending fields when using Copier." def _validate_model(model_class): if not is_model_class(model_class): raise InvalidReceiverError(model_class, 'Invalid model') def _validate_function(model_class, callback_function): if not inspect.isfunction(callback_function) or not callback_function: raise InvalidReceiverError(model_class, 'Invalid function') if callback_function: args = len(inspect.getfullargspec(callback_function).args) if args != 1: raise InvalidReceiverError(model_class, 'Invalid number of function arguments') def set_pre_save_receiver(model_class, callback_function): ''' :model_class: a model_class can have only one receiver. Do not complicate yourself. :callback_function must be a function that receive the instance as unique parameter. ''' _validate_model(model_class) _validate_function(model_class, callback_function) _PRE_SAVE[model_class] = callback_function def set_post_save_receiver(model_class, callback_function): ''' :model_class: a model_class can have only one receiver. Do not complicate yourself. :callback_function must be a function that receive the instance as unique parameter. ''' _validate_model(model_class) _validate_function(model_class, callback_function) _POST_SAVE[model_class] = callback_function class DataFixture: ''' Responsibility: return a valid data for a Django Field, according to its type, model class, constraints etc. You must create a separated method to generate data for an specific field. For a field called 'MyField', the method must be: def myfield_config(self, field, key): return 'some value' :field: Field object. :key: string that represents a unique name for a Field, considering app, model and field names. ''' def __init__(self): self.plugins = {} def _field_fixture_template(self, field_class): return f'{field_class.__name__.lower()}_config' def _field_fixture_factory(self, field_class): try: fixture = self._field_fixture_template(field_class) getattr(self, fixture) return fixture except AttributeError: if len(field_class.__bases__) > 0: # Pick the first parent class that inherits Field (or use the first parent class) field_subclasses = (cls for cls in field_class.__bases__ if issubclass(cls, Field)) parent_class = next(field_subclasses, field_class.__bases__[0]) return self._field_fixture_factory(parent_class) else: return None def generate_data(self, field): '''Get a unique and valid data for the field.''' field_fullname = field.__module__ + "." + field.__class__.__name__ fixture = self.plugins.get(field_fullname, {}) if type(fixture) == dict: fixture = fixture.get('ddf_fixture', None) if fixture and callable(fixture): return fixture() config = self._field_fixture_factory(field.__class__) is_supported_field = config != None if is_supported_field: key = get_unique_field_name(field) data = eval(f'self.{config}(field, "{key}")') else: if field.null: data = None # a workaround for versatility else: raise(UnsupportedFieldError(get_unique_field_name(field) + ' (%s)' % (field_fullname))) return data class Copier: ''' Wrapper of an expression in the format 'field' or 'field.field' or 'field.field.field' etc This expression will be interpreted to copy the value of the specified field to the current field. Example of usage: G(MyModel, x=C('y.z')) => the value 'z' of field 'y' will be copied to field 'x'. ''' def __init__(self, expression): self.expression = expression def __str__(self): return "C('%s')" % self.expression def immediate_field_name(self, instance): model_class = instance.__class__ field_name = self.expression.split('.')[0] get_field_by_name_or_raise(model_class, field_name) return field_name def eval_expression(self, instance): try: current_instance = instance fields = self.expression.split('.') for field in fields: current_instance = getattr(current_instance, field) return current_instance except Exception as e: raise InvalidCopierExpressionError(self.expression, e) class Mask: ''' Wrapper for an expression mask that will be used to generate a random string with a custom format. The expression mask supports 4 special characters: - `#` for random numbers; - `-` for random uppercase chars; - `_` for random lowercase chars; - `!` to escape special chars; Other characters will not be interpreted and it will be considered part of the final string. Example of usage: D('###.___!----') => '510.kap-NGK' ''' def __init__(self, expression): self.expression = expression def __str__(self): return "D('%s')" % self.expression def evaluate(self): import random import string chars = [] escaped = False for char in self.expression: if escaped: c = char escaped = False elif char == '#': c = random.choice(string.digits) elif char == '-': c = random.choice(string.ascii_uppercase) elif char == '_': c = random.choice(string.ascii_lowercase) elif char == '!': escaped = True continue else: c = char chars.append(c) return ''.join(chars) class DDFLibrary: instance = None DEFAULT_KEY = 'ddf_default' def __init__(self): self.configs = {} # {Model: {name: config}}" def __str__(self): return '\n'.join([f'{key} = {value}' for key, value in self.configs.items()]) @classmethod def get_instance(cls): if not cls.instance: cls.instance = DDFLibrary() return cls.instance def add_configuration(self, model_class, kwargs, name=None): import os import warnings if name in [None, True]: name = self.DEFAULT_KEY model_class = self._get_concrete_model(model_class) if model_class in self.configs and name in self.configs[model_class]: if not os.getenv('DDF_SHELL_MODE'): msg = "Override a lesson is an anti-pattern and will turn your test suite very hard to understand." msg = f'A lesson {name} has already been saved for the model {model_class}. {msg}' warnings.warn(msg, RuntimeWarning) model_class_config = self.configs.setdefault(model_class, {}) model_class_config[name] = kwargs def get_configuration(self, model_class, name=None): if name is None: name = self.DEFAULT_KEY model_class = self._get_concrete_model(model_class) # copy is important because this dict will be updated every time in the algorithm. config = self.configs.get(model_class, {}) if name != self.DEFAULT_KEY and name not in config.keys(): raise InvalidConfigurationError('There is no lesson for model {} with the name "{}"'.format(get_unique_model_name(model_class), name)) return config.get(name, {}).copy() # default configuration never raises an error def _get_concrete_model(self, model_class): if hasattr(model_class, '_meta') and model_class._meta.proxy: return model_class._meta.concrete_model or model_class else: return model_class def clear(self): '''Remove all lessons of the library. Util for the DDF tests.''' self.configs = {} def clear_configuration(self, model_class): '''Remove from the library an specific configuration of a model.''' if model_class in self.configs.keys(): del self.configs[model_class] class DynamicFixture: ''' Responsibility: create a valid model instance according to the given configuration. ''' _DDF_CONFIGS = ['fill_nullable_fields', 'ignore_fields', 'data_fixture', 'fk_min_depth', 'validate_models', 'print_errors'] def __init__(self, data_fixture, fill_nullable_fields=False, ignore_fields=[], fk_min_depth=0, validate_models=False, print_errors=True, debug_mode=False, **kwargs): ''' :data_fixture: algorithm to fill field data. :fill_nullable_fields: flag to decide if nullable fields must be filled with data. :ignore_fields: list of field names that must not be filled with data. :fk_min_depth: how deep DDF should go to create non-required FKs fields from the main model. :validate_models: flag to decide if the model_instance.full_clean() must be called before saving the object. :print_errors: flag to determine if the model data must be printed to console on errors. For some scripts is interesting to disable it. ''' from django_dynamic_fixture.global_settings import DDF_IGNORE_FIELDS from django_dynamic_fixture.fixture_algorithms import FixtureFactory # custom config of fixtures self.data_fixture = FixtureFactory.get(data_fixture) self.fill_nullable_fields = fill_nullable_fields self.ignore_fields = ignore_fields # extend ignore_fields with globally declared ignore_fields self.ignore_fields.extend(DDF_IGNORE_FIELDS) self.fk_min_depth = fk_min_depth # other ddfs configs self.validate_models = validate_models self.print_errors = print_errors # internal logic self.pending_fields = [] self.fields_processed = [] self.debug_mode = debug_mode self.kwargs = kwargs self.fields_to_disable_auto_now = [] self.fields_to_disable_auto_now_add = [] def __str__(self): return 'F(%s)' % (', '.join(f'{key}={value}' for key, value in self.kwargs.items())) def __eq__(self, that): return isinstance(that, self.__class__) and self.kwargs == that.kwargs def _get_data_from_custom_dynamic_fixture(self, field, fixture, persist_dependencies): '''return data of a Dynamic Fixture: field=F(...)''' next_model = get_related_model(field) if persist_dependencies: data = fixture.get(next_model) else: data = fixture.new(next_model, persist_dependencies=persist_dependencies) return data def _get_data_from_custom_copier(self, instance, field, fixture): '''return data of a Copier: field=C(...)''' field_name = fixture.immediate_field_name(instance) if field_name in self.fields_processed: data = fixture.eval_expression(instance) else: self.pending_fields.append(field.name) raise PendingField('%s' % field.name) return data def _get_data_from_data_fixture(self, field, fixture): '''return data of a Data Fixture: field=DataFixture()''' next_model = get_related_model(field) return fixture.generate_data(next_model) def _get_data_from_a_custom_function(self, field, fixture): '''Returns data of a custom function: field=lambda field: field.name''' data = fixture(field) return data def _get_data_from_static_data(self, field, fixture): '''return date from a static value: field=3''' if hasattr(field, 'auto_now_add') and field.auto_now_add: self.fields_to_disable_auto_now_add.append(field) if hasattr(field, 'auto_now') and field.auto_now: self.fields_to_disable_auto_now.append(field) return fixture def _process_field_with_customized_fixture(self, instance, field, fixture, persist_dependencies): '''Set a custom value to a field.''' if isinstance(fixture, DynamicFixture): # DynamicFixture (F) data = self._get_data_from_custom_dynamic_fixture(field, fixture, persist_dependencies) elif isinstance(fixture, Copier): # Copier (C) data = self._get_data_from_custom_copier(instance, field, fixture) elif isinstance(fixture, Mask): # Mask (M) data = fixture.evaluate() elif isinstance(fixture, DataFixture): # DataFixture data = self._get_data_from_data_fixture(field, fixture) elif callable(fixture): # callable with the field as parameters data = self._get_data_from_a_custom_function(field, fixture) else: # attribute value data = self._get_data_from_static_data(field, fixture) return data def _process_foreign_key(self, model_class, field, persist_dependencies): ''' Returns auto-generated value for a field ForeignKey or OneToOneField. ''' if field_is_a_parent_link(field): return None next_model = get_related_model(field) # 1. Propagate ignored_fields only for self references if model_class == next_model: # self reference ignore_fields = self.ignore_fields else: ignore_fields = [] # 2. It needs a new DynamicFixture to control the cycles and ignored fields. fixture = DynamicFixture(data_fixture=self.data_fixture, fill_nullable_fields=self.fill_nullable_fields, ignore_fields=ignore_fields, fk_min_depth=self.fk_min_depth - 1, # Depth decreased validate_models=self.validate_models, print_errors=self.print_errors) # 3. Persist it if persist_dependencies: data = fixture.get(next_model) else: data = fixture.new(next_model, persist_dependencies=persist_dependencies) return data def _process_field_with_default_fixture(self, field, model_class, persist_dependencies): ''' The field has no custom value (F, C, static, Lessons...), so the default behavior of the tool is applied. - DDF behavior priority for common fields: 1. Use `null` if possible (considering the `fill_nullable_fields` settings) 2. Use the `default` value 3. Use the first option of `choices` - DDF behavior priority for relationship fields: 1. Use the `default` value 2. Use `null` if possible, or consider the `fk_min_depth` value 3. Create a new FK model ''' if is_relationship_field(field): if field_has_default_value(field): # datetime default can receive a function: datetime.now data = field.default() if callable(field.default) else field.default else: if (not field.null) or self.fk_min_depth > 0: data = self._process_foreign_key(model_class, field, persist_dependencies) else: data = None else: if field_has_default_value(field): # datetime default can receive a function: datetime.now data = field.default() if callable(field.default) else field.default elif field.null and not self.fill_nullable_fields: data = None elif field_has_choices(field): data = field.flatchoices[0][0] # key of the first choice else: data = self.data_fixture.generate_data(field) return data def set_data_for_a_field(self, model_class, __instance, __field, persist_dependencies=True, **kwargs): if __field.name in kwargs: config = kwargs[__field.name] try: data = self._process_field_with_customized_fixture(__instance, __field, config, persist_dependencies) except PendingField: return # ignore this field for a while. except Exception as e: raise InvalidConfigurationError(get_unique_field_name(__field), e) else: data = self._process_field_with_default_fixture(__field, model_class, persist_dependencies) if is_file_field(__field) and data: django_file = data if isinstance(django_file, File): setattr(__instance, __field.name, data.name) # set the attribute if hasattr(django_file.file, 'mode') and django_file.file.mode != 'rb': django_file.file.close() # this file may be open in another mode, for example, in a+b opened_file = open(django_file.file.name, 'rb') # to save the file it must be open in rb mode django_file.file = opened_file # we update the reference to the rb mode opened file # https://github.com/paulocheque/django-dynamic-fixture/issues/10 # getattr(__instance, __field.name).save(django_file.name, django_file) # save the file into the file storage system # django_file.close() getattr(__instance, __field.name).save(django_file.name, django_file, save=False) else: # string (saving just a name in the file, without saving the file to the storage file system setattr(__instance, __field.name, data) # Model.field = data else: if self.debug_mode: LOGGER.debug(f'{get_unique_model_name(model_class)}.{__field.name} = {data}') try: setattr(__instance, __field.name, data) # Model.field = data except (ValueError, AttributeError) as e: if is_relationship_field(__field): # Handle AttributeError for compatibility with django-polymorphic # https://github.com/paulocheque/django-dynamic-fixture/issues/88 field_value = data.id if data and isinstance(e, AttributeError) else data setattr(__instance, "%s_id" % __field.name, field_value) # Model.field = data else: raise e self.fields_processed.append(__field.name) def _validate_kwargs(self, model_class, kwargs): '''validate all kwargs match Model.fields.''' for field_name in kwargs.keys(): if field_name in self._DDF_CONFIGS: continue if not model_has_the_field(model_class, field_name): raise InvalidConfigurationError('Field "%s" does not exist.' % field_name) def _configure_params(self, model_class, ddf_lesson, **kwargs): ''' 1) validate kwargs 2) load default fixture from DDF library. Store default fixture in DDF library. 3) Load fixtures defined in F attributes. ''' self._validate_kwargs(model_class, kwargs) library = DDFLibrary.get_instance() configuration = {} # 1. Load the default/global lesson for the model. configuration_default = library.get_configuration(model_class, name=DDFLibrary.DEFAULT_KEY) configuration.update(configuration_default) # always use default configuration # 2. Load a custom lesson for the model. if ddf_lesson: configuration_custom = library.get_configuration(model_class, name=ddf_lesson) configuration.update(configuration_custom) # override default configuration # 3. Load the custom `kwargs` attributes. configuration.update(kwargs) # override the configuration with current configuration configuration.update(self.kwargs) # Used by F: kwargs are passed by constructor, not by get. return configuration def new(self, model_class, ddf_lesson=None, persist_dependencies=True, **kwargs): ''' Create an instance filled with data without persist it. 1) validate all kwargs match Model.fields. 2) validate model is a model.Model class. 3) Iterate model fields: for each field, fill it with data. :ddf_lesson: the lesson that will be used to create the model instance, if exists. :persist_dependencies: tell if internal dependencies will be saved in the database or not. ''' if self.debug_mode: LOGGER.debug('>>> [%s] Generating instance.' % get_unique_model_name(model_class)) configuration = self._configure_params(model_class, ddf_lesson, **kwargs) try: instance = model_class() except TypeError: raise InvalidModelError(get_unique_model_name(model_class)) if not is_model_class(instance): raise InvalidModelError(get_unique_model_name(model_class)) try: # https://github.com/paulocheque/django-dynamic-fixture/pull/112 from polymorphic import PolymorphicModel is_polymorphic = isinstance(instance, PolymorphicModel) except ImportError: # Django-polymorphic is not installed so the model can't be polymorphic. is_polymorphic = False for field in get_fields_from_model(model_class): if is_key_field(field) and field.name not in configuration: continue if field.name not in self.kwargs and self._is_ignored_field(field.name): continue if is_polymorphic and (field.name == 'polymorphic_ctype' or field.primary_key): continue self.set_data_for_a_field(model_class, instance, field, persist_dependencies=persist_dependencies, **configuration) number_of_pending_fields = len(self.pending_fields) # For Copier fixtures: dealing with pending fields that need to receive values of another fields. i = 0 while self.pending_fields != []: field_name = self.pending_fields.pop(0) field = get_field_by_name_or_raise(model_class, field_name) self.set_data_for_a_field(model_class, instance, field, persist_dependencies=persist_dependencies, **configuration) i += 1 if i > 2 * number_of_pending_fields: # dealing with infinite loop too. raise InvalidConfigurationError(get_unique_field_name(field), 'Cyclic dependency of Copiers.') if self.debug_mode: LOGGER.debug('<<< [%s] Instance created.' % get_unique_model_name(model_class)) return instance def _is_ignored_field(self, field_name): ''' Return `True` if the given field name should be ignored according to this class's `self.ignored_fields`. Both literal field names and names with wildcard '*' and '?' characters are supported. ''' # Do fast check for literal field name first if field_name in self.ignore_fields: return True # If any ignored field names contain wildcards, check them against the # given field name for ignore_spec in self.ignore_fields: if '*' in ignore_spec or '?' in ignore_spec: # Replace wildcard characters with regexp equivalents re_spec = ignore_spec.replace('?', '.').replace('*', '.*') # Update regexp to match entire field name, not just a portion re_spec = r'^%s$' % re_spec if re.match(re_spec, field_name): return True return False def _process_many_to_many_field(self, field, manytomany_field, fixture, instance): ''' Set ManyToManyField fields with or without 'trough' option. :field: model field. :manytomany_field: ManyRelatedManager of the field. :fixture: value passed by user. ''' next_model = get_related_model(field) if isinstance(fixture, int): amount = fixture for _ in range(amount): next_instance = self.get(next_model) self._create_manytomany_relationship(manytomany_field, instance, next_instance) elif isinstance(fixture, (list, tuple)): items = fixture for item in items: if isinstance(item, DynamicFixture): next_instance = item.get(next_model, **item.kwargs) # need to pass F.kwargs recursively. else: next_instance = item self._create_manytomany_relationship(manytomany_field, instance, next_instance) else: raise InvalidManyToManyConfigurationError('Field: %s' % field.name, str(fixture)) def _create_manytomany_relationship(self, manytomany_field, instance, next_instance): try: manytomany_field.add(next_instance) except AttributeError: next_instance.save() # Create an instance of the "through" model using the current data fixture through_model = manytomany_field.through through_instance = DynamicFixture(data_fixture=self.data_fixture) \ .get(through_model, **{ manytomany_field.source_field_name: instance, manytomany_field.target_field_name: next_instance }) def _save_the_instance(self, instance): for field in self.fields_to_disable_auto_now: disable_auto_now(field) for field in self.fields_to_disable_auto_now_add: disable_auto_now_add(field) instance.save() for field in self.fields_to_disable_auto_now: enable_auto_now(field) for field in self.fields_to_disable_auto_now_add: enable_auto_now_add(field) def get(self, model_class, ddf_lesson=None, **kwargs): ''' Create an instance with data and persist it. :ddf_lesson: a custom lesson that will be used to create the model object. ''' instance = self.new(model_class, ddf_lesson=ddf_lesson, **kwargs) if is_model_abstract(model_class): raise InvalidModelError(get_unique_model_name(model_class)) try: if self.validate_models: instance.full_clean() if model_class in _PRE_SAVE: try: _PRE_SAVE[model_class](instance) except Exception as e: raise InvalidReceiverError(e) self._save_the_instance(instance) if model_class in _POST_SAVE: try: _POST_SAVE[model_class](instance) except Exception as e: raise InvalidReceiverError(e) except Exception as e: if self.print_errors: print_field_values(instance) raise BadDataError(get_unique_model_name(model_class), e) self.fields_processed = [] # TODO: need more tests for M2M and Copier self.pending_fields = [] for field in get_many_to_many_fields_from_model(model_class): if field.name in kwargs.keys(): # TODO: library manytomany_field = getattr(instance, field.name) fixture = kwargs[field.name] try: self._process_many_to_many_field(field, manytomany_field, fixture, instance) except InvalidManyToManyConfigurationError as e: raise e except Exception as e: raise InvalidManyToManyConfigurationError(get_unique_field_name(field), e) return instance def teach(self, model_class, ddf_lesson=None, **kwargs): library = DDFLibrary.get_instance() for field_name in kwargs.keys(): if field_name in self._DDF_CONFIGS: continue field = get_field_by_name_or_raise(model_class, field_name) if field.unique and not _is_dynamic_value(kwargs[field_name]): raise InvalidConfigurationError('It is not possible to store static values for fields with unique=True (%s). Try using a lambda function instead.' % get_unique_field_name(field)) library.add_configuration(model_class, kwargs, name=ddf_lesson) def _is_dynamic_value(fixture): return isinstance(fixture, (DynamicFixture, Copier, DataFixture, Mask)) or callable(fixture) django-dynamic-fixture-4.0.1/django_dynamic_fixture/decorators.py000066400000000000000000000013161450115615700253250ustar00rootroot00000000000000 from django.conf import settings DATABASE_ENGINE = settings.DATABASES['default']['ENGINE'] SQLITE3 = 'sqlite3' POSTGRES = 'postgresql' MYSQL = 'mysql' ORACLE = 'oracle' SQLSERVER = 'pyodbc' def skip_for_database(database): def main_decorator(testcase_function): def wrapper(*args, **kwargs): if database not in DATABASE_ENGINE: testcase_function(*args, **kwargs) return wrapper return main_decorator def only_for_database(database): def main_decorator(testcase_function): def wrapper(*args, **kwargs): if database in DATABASE_ENGINE: testcase_function(*args, **kwargs) return wrapper return main_decorator django-dynamic-fixture-4.0.1/django_dynamic_fixture/django_helper.py000066400000000000000000000171071450115615700257660ustar00rootroot00000000000000""" Module to wrap dirty stuff of django core. """ import django from django.apps import apps from django.db import models # noqa from django.db.models import * from django.db.models.fields import NOT_PROVIDED, AutoField from django.db.models.base import ModelBase from django.db.models.query import QuerySet try: from django.db.models.fields import FieldDoesNotExist except ImportError: from django.core.exceptions import FieldDoesNotExist def django_greater_than(major, minor=0): return django.VERSION[:2] > (major, minor) # Apps def get_apps(application_labels=[], exclude_application_labels=[]): """ - if not @application_labels and not @exclude_application_labels, it returns all applications. - if @application_labels is not None, it returns just these applications, except applications with label in exclude_application_labels. @Returns an array of application labels. """ if application_labels: applications = [] for app_label in application_labels: app_config = apps.get_app_config(app_label) applications.append(app_config.label) else: applications = [ app_config.label for app_config in apps.get_app_configs() ] if exclude_application_labels: for app_label in exclude_application_labels: if app_label: if app_label in applications: applications.remove(app_label) else: raise ValueError( f"Excluded application with label '{app_label}' " "is not installed.") return applications def get_app_name(app_module): """ app is the object (python module) returned by get_apps method """ return app_module.__name__.split('.')[0] def get_models_of_an_app(app_label): """ app_module is the object returned by get_apps method (python module) """ app_config = apps.get_app_config(app_label) return list(app_config.get_models()) # Models def get_app_name_of_model(model_class): return model_class.__module__.split('.')[0] def get_model_name(model_class): "Example: ModelName" return model_class.__name__ def get_unique_model_name(model_class): "Example: app.packages.ModelName" return model_class.__module__ + '.' + model_class.__name__ def get_fields_from_model(model_class): "Returns all fields, including inherited fields but ignoring M2M fields." return model_class._meta.fields def get_local_fields(model): "Returns all local fields!?" return model._meta.local_fields def get_many_to_many_fields_from_model(model_class): "Return only M2M fields, including inherited ones?" return model_class._meta.many_to_many #_meta.local_many_to_many def get_all_fields_of_model(model_class): fields1 = get_fields_from_model(model_class) fields2 = get_many_to_many_fields_from_model(model_class) fields1.extend(fields2) return fields1 def get_field_names_of_model(model_class): "Get field names, including inherited fields, except M2M fields." fields = get_fields_from_model(model_class) return [field.name for field in fields] def get_field_by_name_or_raise(model_class, field_name): "Get field by name, including inherited fields and M2M fields." return model_class._meta.get_field(field_name) def is_model_class(instance_or_model_class): "True if model_class is a Django Model." return isinstance(instance_or_model_class, Model) or instance_or_model_class.__class__ == ModelBase def is_model_abstract(model): "True if abstract is True in Meta class" return model._meta.abstract def is_model_managed(model): "True if managed is True in Meta class" return model._meta.managed def model_has_the_field(model_class, field_name): "" try: get_field_by_name_or_raise(model_class, field_name) return True except FieldDoesNotExist: return False # Fields def get_unique_field_name(field): if hasattr(field, 'model'): return get_unique_model_name(field.model) + '.' + field.name return field.name or '' def get_related_model(field): return field.remote_field.model if hasattr(field, 'remote_field') else field.rel.to def field_is_a_parent_link(field): # FIXME #return hasattr(field, 'rel') and hasattr(field.rel, 'parent_link') and field.rel.parent_link return hasattr(field, 'parent_link') and field.parent_link def field_has_choices(field): """field.choices may be a tee, which we can't count without converting it to a list, or it may be a large database queryset, in which case we don't want to convert it to a list. We only care if the list is empty or not, so just try to access the first element and return True if that doesn't throw an exception.""" if not field.choices: return False for i in field.choices: return True return False def field_has_default_value(field): return field.default != NOT_PROVIDED def field_is_unique(field): return field.unique def is_key_field(field): return isinstance(field, AutoField) def is_relationship_field(field): return isinstance(field, (ForeignKey, OneToOneField)) def is_file_field(field): return isinstance(field, FileField) def print_field_values_of_a_model(model_instance): "Print values from all fields of a model instance." if model_instance == None: print('\n:: Model Unknown: None') else: print('\n:: Model %s (%s)' % (get_unique_model_name(model_instance.__class__), model_instance.pk)) for field in get_fields_from_model(model_instance.__class__): try: value = getattr(model_instance, field.name) except Exception as e: value = repr(e) print('%s: %s' % (field.name, value)) if model_instance.pk is not None: for field in get_many_to_many_fields_from_model(model_instance.__class__): print('%s: %s' % (field.name, getattr(model_instance, field.name).all())) def print_field_values(model_instance_or_list_of_model_instances_or_queryset): "Print values from all fields of a model instance or a list of model instances." if isinstance(model_instance_or_list_of_model_instances_or_queryset, (list, tuple, QuerySet)): for model_instance in model_instance_or_list_of_model_instances_or_queryset: print_field_values_of_a_model(model_instance) else: model_instance = model_instance_or_list_of_model_instances_or_queryset print_field_values_of_a_model(model_instance) def enable_auto_now(field): if hasattr(field, 'auto_now'): field.auto_now = True def disable_auto_now(field): if hasattr(field, 'auto_now'): field.auto_now = False def enable_auto_now_add(field): if hasattr(field, 'auto_now_add'): field.auto_now_add = True def disable_auto_now_add(field): if hasattr(field, 'auto_now_add'): field.auto_now_add = False def is_boolean(field): return isinstance(field, (BooleanField, NullBooleanField)) def is_string(field): return isinstance(field, (CharField, EmailField, IPAddressField, SlugField, URLField)) def is_number(field): return isinstance(field, (IntegerField, SmallIntegerField, PositiveIntegerField, PositiveSmallIntegerField, BigIntegerField, CommaSeparatedIntegerField, DecimalField, FloatField)) def is_datetime(field): return isinstance(field, (DateTimeField, DateField, TimeField)) def is_file(field): return isinstance(field, (FileField, FilePathField)) def is_binary(field): return isinstance(field, (BinaryField)) django-dynamic-fixture-4.0.1/django_dynamic_fixture/fdf.py000066400000000000000000000160201450115615700237150ustar00rootroot00000000000000 import os import tempfile from shutil import rmtree, copy2 from django.core.files import File from django.test import TestCase from django.conf import settings from django.core.files.storage import FileSystemStorage TEMP_PATH = tempfile.gettempdir() or os.environ.get('TEMP') TEMP_PATH_DDF = os.path.join(TEMP_PATH, 'DDF_TEMP') class CustomFileSystemStorage(FileSystemStorage): def __init__(self, *args, **kwargs): super().\ __init__(location=TEMP_PATH_DDF, *args, **kwargs) class FileSystemDjangoTestCase(TestCase): TEAR_DOWN_ENABLED = True def setUp(self): self.fdf_setup() def tearDown(self): self.fdf_teardown() def _pre_setup(self): super()._pre_setup() self.fdf_setup() def _post_teardown(self): "Try to remove all files and directories created by the test." super()._post_teardown() self.fdf_teardown() def fdf_setup(self): self.directories = [] self.files = {} setattr(settings, 'DEFAULT_FILE_STORAGE', 'django_dynamic_fixture.fdf.CustomFileSystemStorage') def fdf_teardown(self): if self.TEAR_DOWN_ENABLED: while self.files: self.remove_temp_file(next(iter(self.files.keys()))) while self.directories: self.remove_temp_directory(self.directories[0]) if os.path.exists(TEMP_PATH_DDF): rmtree(TEMP_PATH_DDF) def create_temp_directory(self, prefix='file_system_test_case_dir_'): "Create a temporary directory and returns the directory pathname." directory = tempfile.mkdtemp(prefix=prefix) self.directories.append(directory) return directory def remove_temp_directory(self, directory_pathname): "Remove a directory." rmtree(directory_pathname) if directory_pathname in self.directories: try: self.directories.remove(directory_pathname) except WindowsError: pass def create_temp_file(self, directory=None, prefix='file_system_test_case_file_', suffix='.tmp'): """ Create a temporary file with a option prefix and suffix in a temporary or custom directory. Returns the filepath """ tmp_file = tempfile.mkstemp(prefix=prefix, dir=directory, suffix=suffix) file_obj = os.fdopen(tmp_file[0]) self.files[tmp_file[1]] = file_obj return tmp_file[1] def create_temp_file_with_name(self, directory, name): "Create a temporary file with a specified name." filepath = os.path.join(directory, name) file_obj = open(filepath, 'wb') file_obj.close() self.files[filepath] = file_obj return filepath def rename_temp_file(self, filepath, name): "Rename an existent file. 'name' is not a file path, so it must not include the directory path name." directory = self.get_directory_of_the_file(filepath) new_filepath = os.path.join(directory, name) os.rename(filepath, new_filepath) if filepath in self.files.keys(): self.files.pop(filepath) self.files[new_filepath] = open(new_filepath, 'a+b') self.files[new_filepath].close() return new_filepath def remove_temp_file(self, filepath): "Remove a file." if filepath in self.files.keys(): fileobj = self.files.pop(filepath) fileobj.close() if os.path.exists(filepath): try: os.unlink(filepath) except WindowsError: pass def copy_file_to_dir(self, filepath, directory): "Copy a file to a specified directory." copy2(filepath, directory) return self.get_filepath(directory, self.get_filename(filepath)) def add_text_to_file(self, filepath, content): "Add text to an existent file." file = open(filepath, 'a') file.write(content) file.close() def get_directory_of_the_file(self, filepath): "Get the directory path name of a file." return os.path.dirname(filepath) def get_filename(self, filepath): "Get the filename of a file." return os.path.basename(filepath) def get_filepath(self, directory, filename): "Get the file path of a file with a defined name in a directory." return os.path.join(directory, filename) def get_content_of_file(self, filepath): "Returns the content of a file." file = open(filepath, 'r') content = file.read() file.close() return content def create_django_file_with_temp_file(self, name, content=None, dir=None, prefix='file_system_test_case_file_', suffix='.tmp'): "Create and returns a django.core.files.File" file = open(self.create_temp_file(directory=dir, prefix=prefix, suffix=suffix), 'w') file.close() django_file = File(file, name=name) self.files[django_file.file.name] = open(django_file.file.name, 'a+b') if content: self.files[django_file.file.name].write(content) self.files[django_file.file.name].close() return django_file def create_django_file_using_file(self, filepath): "Create and returns a django.core.files.File" new_filepath = self.copy_file_to_dir(filepath, self.create_temp_directory()) the_file = open(new_filepath, 'rb') django_file = File(the_file, name=os.path.basename(new_filepath)) self.files[django_file.file.name] = the_file #self.files[django_file.file.name].close() return django_file def assertFileExists(self, filepath): assert os.path.exists(filepath), '%s does not exist' % filepath def assertFileDoesNotExists(self, filepath): assert os.path.exists(filepath) is False, '%s exist' % filepath def assertDirectoryExists(self, directory): "@directory must be the directory path" assert os.path.exists(directory), '%s does not exist' % directory def assertDirectoryDoesNotExists(self, directory): "@directory must be the directory path" assert os.path.exists(directory) is False, '%s exist' % directory def assertDirectoryContainsFile(self, directory, filename): filepath = os.path.join(directory, filename) self.assertFileExists(filepath) def assertDirectoryDoesNotContainsFile(self, directory, filename): filepath = os.path.join(directory, filename) self.assertFileDoesNotExists(filepath) def assertFilesHaveEqualLastModificationTimestamps(self, filepath1, filepath2): assert os.path.getmtime(filepath1) - os.path.getmtime(filepath2) == 0 def assertFilesHaveNotEqualLastModificationTimestamps(self, filepath1, filepath2): assert os.path.getmtime(filepath1) - os.path.getmtime(filepath2) != 0 def assertNumberOfFiles(self, directory, number_of_files): filenames = [filename for filename in os.listdir(directory) if os.path.isfile(os.path.join(directory, filename))] assert len(filenames) == number_of_files, '[%s] %s' % (len(filenames), filenames) django-dynamic-fixture-4.0.1/django_dynamic_fixture/fields.py000066400000000000000000000032161450115615700244270ustar00rootroot00000000000000import json from django.conf import settings from django.contrib.postgres.fields import ( JSONField as DjangoJSONField, ArrayField as DjangoArrayField, ) from django.db.models import Field class JSONField(DjangoJSONField): pass class ArrayField(DjangoArrayField): pass if 'sqlite' in settings.DATABASES['default']['ENGINE']: class JSONField(Field): def db_type(self, connection): return 'text' def from_db_value(self, value, expression, connection): if value is not None: return self.to_python(value) return value def to_python(self, value): if value is not None: try: return json.loads(value) except (TypeError, ValueError): return value return value def get_prep_value(self, value): if value is not None: return str(json.dumps(value)) return value def value_to_string(self, obj): return self.value_from_object(obj) class ArrayField(JSONField): def __init__(self, base_field, size=None, **kwargs): """Care for DjanroArrayField's kwargs.""" self.base_field = base_field self.size = size return super().__init__(**kwargs) def deconstruct(self): """Need to create migrations properly.""" name, path, args, kwargs = super().deconstruct() kwargs.update({ 'base_field': self.base_field.clone(), 'size': self.size, }) return name, path, args, kwargs django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/000077500000000000000000000000001450115615700265245ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/__init__.py000066400000000000000000000010621450115615700306340ustar00rootroot00000000000000from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, StaticSequentialDataFixture from django_dynamic_fixture.fixture_algorithms.random_fixture import RandomDataFixture class FixtureFactory: @staticmethod def get(data_fixture): if data_fixture == 'static_sequential': return SequentialDataFixture() elif data_fixture == 'sequential': return SequentialDataFixture() elif data_fixture == 'random': return RandomDataFixture() return data_fixturedjango-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/default_fixture.py000066400000000000000000000052751450115615700323010ustar00rootroot00000000000000# -*- coding: utf-8 -*- from datetime import datetime, date, timedelta from decimal import Decimal import random import string import uuid from django.core.exceptions import ImproperlyConfigured try: from django.utils.timezone import now except ImportError: now = datetime.now try: from django.contrib.gis.geos import * except ImproperlyConfigured: pass # environment without geo libs except Exception: pass # Avoid errors like GDALException from django_dynamic_fixture.ddf import DataFixture class BaseDataFixture(DataFixture): # Django >= 1.6 def binaryfield_config(self, field, key): return b'\x00\x46\xFE' # Django >= 1.8 def uuidfield_config(self, field, key): return uuid.uuid4() # Django >= 1.4 def genericipaddressfield_config(self, field, key): return self.ipaddressfield_config(field, key) # POSTGRES def jsonfield_config(self, field, key): return {} # GIS/GeoDjango class GeoDjangoFixtureMixin: def create_point(self, x=None, y=None): # latitude: [-90,90], longitude: [-180,180] latitude = x or random.randint(-90, 90) longitude = y or random.randint(-180, 180) return Point(longitude, latitude) def create_points(self, n=3, closed=True): points = [self.create_point() for i in range(n)] if closed: # LinearRing points.append(points[0]) return points def geometryfield_config(self, field, key): return GEOSGeometry('POINT(%s %s)' % self.create_point().coords) def pointfield_config(self, field, key): return self.create_point() def linestringfield_config(self, field, key, n=3): return LineString(self.create_points(n)) def polygonfield_config(self, field, key, n=3): return Polygon(self.create_points(n)) def multipointfield_config(self, field, key, n=3): return MultiPoint(self.create_points(n)) def multilinestringfield_config(self, field, key, n=3): lines = [self.linestringfield_config(field, key, n) for i in range(n)] return MultiLineString(lines) def multipolygonfield_config(self, field, key, n=3): polygons = [self.polygonfield_config(field, key, n) for i in range(n)] return MultiPolygon(polygons) def geometrycollectionfield_config(self, field, key, n=3): polygons = [self.polygonfield_config(field, key, n) for i in range(n)] return GeometryCollection(polygons) # Postgres fields # https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/ class PostgresFixtureMixin: def arrayfield_config(self, field, key, n=1): data = [self.generate_data(field.base_field) for i in range(n)] return data django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/random_fixture.py000066400000000000000000000075201450115615700321300ustar00rootroot00000000000000from datetime import datetime, date, timedelta from decimal import Decimal import random import string from django.core.exceptions import ImproperlyConfigured try: from django.utils.timezone import now except ImportError: now = datetime.now try: from django.contrib.gis.geos import * except ImproperlyConfigured: pass # environment without geo libs except Exception: pass # Avoid errors like GDALException from django_dynamic_fixture.fixture_algorithms.default_fixture import BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin class RandomDataFixture(BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin): def random_string(self, n): return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(n)) # NUMBERS def integerfield_config(self, field, key, start=1, end=10 ** 6): return random.randint(start, end) def smallintegerfield_config(self, field, key): # Values from -32768 to 32767 are safe in all databases supported by Django. return self.integerfield_config(field, key, -2 ** 15, 2 ** 15 - 1) def positiveintegerfield_config(self, field, key): return self.integerfield_config(field, key) def positivesmallintegerfield_config(self, field, key): # Values up to 32767 are safe in all databases supported by Django. return self.integerfield_config(field, key, end=2 ** 15 - 1) def bigintegerfield_config(self, field, key): return self.integerfield_config(field, key) def floatfield_config(self, field, key): return float(self.integerfield_config(field, key)) def decimalfield_config(self, field, key): data = self.integerfield_config(field, key) number_of_digits = field.max_digits - field.decimal_places max_value = 10 ** number_of_digits data = data % max_value return Decimal(str(data)) # STRINGS def charfield_config(self, field, key): if field.max_length: length = field.max_length else: length = 10 return self.random_string(length) def textfield_config(self, field, key): return self.charfield_config(field, key) def slugfield_config(self, field, key): return self.charfield_config(field, key) def commaseparatedintegerfield_config(self, field, key): return str(random.randint(1, field.max_length)) #FIXME: # BOOLEAN def booleanfield_config(self, field, key): return random.randint(0, 1) == 0 def nullbooleanfield_config(self, field, key): values = {0: None, 1: False, 2: True} return values[random.randint(0, 2)] # DATE/TIME RELATED def datefield_config(self, field, key): return date.today() - timedelta(days=random.randint(1, 36500)) def timefield_config(self, field, key): return (now() - timedelta(seconds=random.randint(1, 36500))).time() def datetimefield_config(self, field, key): return now() - timedelta(seconds=random.randint(1, 36500)) # FORMATTED STRINGS def emailfield_config(self, field, key): return f'a{self.random_string(10)}@dynamicfixture.com' def urlfield_config(self, field, key): return f'http://dynamicfixture{self.random_string(10)}.com' # Deprecated in Django >= 1.7 def ipaddressfield_config(self, field, key): a = random.randint(1, 255) b = random.randint(1, 255) c = random.randint(1, 255) d = random.randint(1, 255) return f'{a}.{b}.{c}.{d}' def xmlfield_config(self, field, key): return f'{self.random_string(5)}' # FILES def filepathfield_config(self, field, key): return self.random_string(10) def filefield_config(self, field, key): return self.random_string(10) def imagefield_config(self, field, key): return self.random_string(10) django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/sequential_fixture.py000066400000000000000000000117471450115615700330300ustar00rootroot00000000000000from datetime import datetime, date, timedelta from decimal import Decimal import threading from django.core.exceptions import ImproperlyConfigured try: from django.utils.timezone import now except ImportError: now = datetime.now try: from django.contrib.gis.geos import * except ImproperlyConfigured: pass # environment without geo libs except Exception: pass # Avoid errors like GDALException from django_dynamic_fixture.fixture_algorithms.default_fixture import BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin from django_dynamic_fixture.django_helper import field_is_unique class AutoDataFiller: """ Responsibility: generate a unique and sequential value for each key. """ def __init__(self): self.__data_controller_map = {} # key => counter self.__locks = {} # key => lock # synchronized by key def next(self, key): if key not in self.__data_controller_map: self.__data_controller_map[key] = 0 self.__locks[key] = threading.RLock() self.__locks[key].acquire() self.__data_controller_map[key] += 1 value = self.__data_controller_map[key] self.__locks[key].release() return value def current(self, key): if key not in self.__data_controller_map: self.next(key) return self.__data_controller_map[key] class SequentialDataFixture(BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin): def __init__(self): super().__init__() self.filler = AutoDataFiller() def get_value(self, field, key): return self.filler.next(key) # NUMBERS def integerfield_config(self, field, key): return self.get_value(field, key) def smallintegerfield_config(self, field, key): return self.integerfield_config(field, key) def positiveintegerfield_config(self, field, key): return self.integerfield_config(field, key) def positivesmallintegerfield_config(self, field, key): return self.integerfield_config(field, key) def bigintegerfield_config(self, field, key): return self.integerfield_config(field, key) def floatfield_config(self, field, key): return float(self.get_value(field, key)) def decimalfield_config(self, field, key): data = self.get_value(field, key) number_of_digits = field.max_digits - field.decimal_places max_value = 10 ** number_of_digits data = data % max_value return Decimal(str(data)) # STRINGS def charfield_config(self, field, key): data = self.get_value(field, key) if field.max_length: max_value = (10 ** field.max_length) - 1 data = str(data % max_value) data = data[:field.max_length] else: data = str(data) return data def textfield_config(self, field, key): return self.charfield_config(field, key) def slugfield_config(self, field, key): return self.charfield_config(field, key) def commaseparatedintegerfield_config(self, field, key): return self.charfield_config(field, key) # BOOLEAN def booleanfield_config(self, field, key): return False def nullbooleanfield_config(self, field, key): return None # DATE/TIME RELATED def datefield_config(self, field, key): data = self.get_value(field, key) return date.today() - timedelta(days=data) def timefield_config(self, field, key): data = self.get_value(field, key) return (now() - timedelta(seconds=data)).time() def datetimefield_config(self, field, key): data = self.get_value(field, key) return now() - timedelta(seconds=data) # FORMATTED STRINGS def emailfield_config(self, field, key): return f'a{self.get_value(field, key)}@dynamicfixture.com' def urlfield_config(self, field, key): return f'http://dynamicfixture{self.get_value(field, key)}.com' # Deprecated in Django >= 1.7 def ipaddressfield_config(self, field, key): # TODO: better workaround (this suppose ip field is not unique) data = self.get_value(field, key) a = '1' b = '1' c = '1' d = data % 256 return f'{a}.{b}.{c}.{d}' def xmlfield_config(self, field, key): return f'{self.get_value(field, key)}' # FILES def filepathfield_config(self, field, key): return str(self.get_value(field, key)) def filefield_config(self, field, key): return str(self.get_value(field, key)) def imagefield_config(self, field, key): return str(self.get_value(field, key)) class GlobalSequentialDataFixture(SequentialDataFixture): def get_value(self, field, key): return self.filler.next('ddf-global-key') class StaticSequentialDataFixture(SequentialDataFixture): def get_value(self, field, key): if field_is_unique(field): return self.filler.next(key) else: return self.filler.current(key) django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/tests/000077500000000000000000000000001450115615700276665ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/tests/__init__.py000066400000000000000000000000011450115615700317660ustar00rootroot00000000000000 abstract_test_generic_fixture.py000066400000000000000000000063011450115615700362650ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/tests from django.db import models from datetime import datetime, date, time from decimal import Decimal class DataFixtureTestCase: def setUp(self): self.fixture = None def test_numbers(self): assert isinstance(self.fixture.generate_data(models.IntegerField()), int) assert isinstance(self.fixture.generate_data(models.SmallIntegerField()), int) assert isinstance(self.fixture.generate_data(models.PositiveIntegerField()), int) assert isinstance(self.fixture.generate_data(models.PositiveSmallIntegerField()), int) assert isinstance(self.fixture.generate_data(models.BigIntegerField()), int) assert isinstance(self.fixture.generate_data(models.FloatField()), float) assert isinstance(self.fixture.generate_data(models.DecimalField(max_digits=1, decimal_places=1)), Decimal) def test_it_must_deal_with_decimal_max_digits(self): # value 10 must be a problem, need to restart the counter: 10.0 has 3 digits for _ in range(11): assert isinstance(self.fixture.generate_data(models.DecimalField(max_digits=1, decimal_places=1)), Decimal) assert isinstance(self.fixture.generate_data(models.DecimalField(max_digits=2, decimal_places=1)), Decimal) def test_strings(self): assert isinstance(self.fixture.generate_data(models.CharField(max_length=1)), str) assert isinstance(self.fixture.generate_data(models.TextField()), str) assert isinstance(self.fixture.generate_data(models.SlugField(max_length=1)), str) assert isinstance(self.fixture.generate_data(models.CommaSeparatedIntegerField(max_length=1)), str) def test_new_truncate_strings_to_max_length(self): for _ in range(12): # truncate start after the 10 object assert isinstance(self.fixture.generate_data(models.CharField(max_length=1)), str) def test_boolean(self): assert isinstance(self.fixture.generate_data(models.BooleanField()), bool) value = self.fixture.generate_data(models.NullBooleanField()) assert isinstance(value, bool) or value is None def test_date_time_related(self): assert isinstance(self.fixture.generate_data(models.DateField()), date) assert isinstance(self.fixture.generate_data(models.TimeField()), time) assert isinstance(self.fixture.generate_data(models.DateTimeField()), datetime) def test_formatted_strings(self): assert isinstance(self.fixture.generate_data(models.EmailField(max_length=100)), str) assert isinstance(self.fixture.generate_data(models.URLField(max_length=100)), str) assert isinstance(self.fixture.generate_data(models.IPAddressField(max_length=100)), str) assert isinstance(self.fixture.generate_data(models.GenericIPAddressField(max_length=100)), str) def test_files(self): assert isinstance(self.fixture.generate_data(models.FilePathField(max_length=100)), str) assert isinstance(self.fixture.generate_data(models.FileField()), str) try: import pil # just test it if the PIL package is installed assert isinstance(self.fixture.generate_data(models.ImageField(max_length=100)), str) except ImportError: pass django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/tests/test_default_fixture.py000066400000000000000000000043171450115615700344760ustar00rootroot00000000000000import uuid from django.db import models from django.conf import settings from django.core.exceptions import ImproperlyConfigured try: from django.contrib.gis.geos import * from django.contrib.gis.db import models as geomodels except ImproperlyConfigured: pass # environment without geo libs from django.test import TestCase from django_dynamic_fixture.fixture_algorithms.default_fixture import BaseDataFixture class BaseDataFixtureTestCase(TestCase): def setUp(self): self.fixture = BaseDataFixture() def test_uuid(self): assert isinstance(self.fixture.generate_data(models.UUIDField()), uuid.UUID) if (hasattr(settings, 'DDF_TEST_GEODJANGO') and settings.DDF_TEST_GEODJANGO): from django_dynamic_fixture.fixture_algorithms.default_fixture import GeoDjangoFixtureMixin # Mixing for tests class GeoDjangoFixtureMixin(BaseDataFixture, GeoDjangoFixtureMixin): pass class GeoDjangoDataFixtureTestCase(TestCase): def setUp(self): self.fixture = GeoDjangoFixtureMixin() def test_geometryfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.GeometryField()), GEOSGeometry) def test_pointfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.PointField()), Point) def test_linestringfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.LineStringField()), LineString) def test_polygonfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.PolygonField()), Polygon) def test_multipointfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.MultiPointField()), MultiPoint) def test_multilinesstringfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.MultiLineStringField()), MultiLineString) def test_multipolygonfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.MultiPolygonField()), MultiPolygon) def test_geometrycollectionfield_config(self): assert isinstance(self.fixture.generate_data(geomodels.GeometryCollectionField()), GeometryCollection) test_default_fixture_postgres.py000066400000000000000000000051461450115615700363460ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/testsfrom datetime import datetime from django.db import models from django.test import TestCase from django_dynamic_fixture.fixture_algorithms.default_fixture import PostgresFixtureMixin from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, StaticSequentialDataFixture from django_dynamic_fixture.fixture_algorithms.random_fixture import RandomDataFixture from django_dynamic_fixture.fixture_algorithms.unique_random_fixture import UniqueRandomDataFixture try: import psycopg2 from django.contrib.postgres.fields import ArrayField class PostgresDataFixtureTestMixin: def test_arrayfield_integer_config(self): data = self.fixture.generate_data(ArrayField(models.IntegerField())) assert isinstance(data, list) assert isinstance(data[0], int) def test_arrayfield_char_config(self): data = self.fixture.generate_data(ArrayField(models.CharField())) assert isinstance(data, list) assert isinstance(data[0], str) def test_arrayfield_datetime_config(self): data = self.fixture.generate_data(ArrayField(models.DateTimeField())) assert isinstance(data, list) assert isinstance(data[0], datetime) def test_arrayfield_email_config(self): data = self.fixture.generate_data(ArrayField(models.EmailField(max_length=100))) assert isinstance(data, list) assert isinstance(data[0], str) class PostgresSequentialDataFixtureTestCase(TestCase, PostgresDataFixtureTestMixin): def setUp(self): class CustomFixture(SequentialDataFixture, PostgresFixtureMixin): pass self.fixture = CustomFixture() class PostgresStaticSequentialDataFixtureTestCase(TestCase, PostgresDataFixtureTestMixin): def setUp(self): class CustomFixture(StaticSequentialDataFixture, PostgresFixtureMixin): pass self.fixture = CustomFixture() class PostgresRandomDataFixtureTestCase(TestCase, PostgresDataFixtureTestMixin): def setUp(self): class CustomFixture(RandomDataFixture, PostgresFixtureMixin): pass self.fixture = CustomFixture() class PostgresUniqueRandomDataFixtureTestCase(TestCase, PostgresDataFixtureTestMixin): def setUp(self): class CustomFixture(UniqueRandomDataFixture, PostgresFixtureMixin): pass self.fixture = CustomFixture() except (ImportError, ModuleNotFoundError): print('Skipping Postgres tests because psycopg2 has not been installed.') django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/tests/test_random_fixture.py000066400000000000000000000005521450115615700343270ustar00rootroot00000000000000 from django.test import TestCase from django_dynamic_fixture.fixture_algorithms.tests.abstract_test_generic_fixture import DataFixtureTestCase from django_dynamic_fixture.fixture_algorithms.random_fixture import RandomDataFixture class RandomDataFixtureTestCase(TestCase, DataFixtureTestCase): def setUp(self): self.fixture = RandomDataFixture() test_sequential_fixture.py000066400000000000000000000034021450115615700351370ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/testsfrom django.db import models from django.test import TestCase from django_dynamic_fixture.fixture_algorithms.tests.abstract_test_generic_fixture import DataFixtureTestCase from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, StaticSequentialDataFixture class SequentialDataFixtureTestCase(TestCase, DataFixtureTestCase): def setUp(self): self.fixture = SequentialDataFixture() def test_it_must_fill_integer_fields_sequencially_by_attribute(self): assert self.fixture.generate_data(models.IntegerField()) == 1 field = models.IntegerField() field.name = 'x' assert self.fixture.generate_data(field) == 1 assert self.fixture.generate_data(field) == 2 def test_it_must_fill_string_with_sequences_of_numbers_by_attribute(self): assert self.fixture.generate_data(models.CharField(max_length=1)) == '1' field = models.CharField(max_length=1) field.name = 'x' assert self.fixture.generate_data(field) == '1' assert self.fixture.generate_data(field) == '2' class StaticSequentialDataFixtureTestCase(TestCase, DataFixtureTestCase): def setUp(self): self.fixture = StaticSequentialDataFixture() def test_it_must_fill_fields_sequencially_by_attribute_if_field_is_unique(self): field = models.IntegerField(unique=True) field.name = 'x' assert self.fixture.generate_data(field) == 1 assert self.fixture.generate_data(field) == 2 def test_it_must_fill_fields_with_static_value_by_attribute_if_field_is_not_unique(self): field = models.IntegerField(unique=False) field.name = 'x' assert self.fixture.generate_data(field) == 1 assert self.fixture.generate_data(field) == 1 test_unique_random_fixture.py000066400000000000000000000037231450115615700356410ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/testsfrom warnings import catch_warnings from django.db import models from django.test import TestCase from django_dynamic_fixture.fixture_algorithms.tests.abstract_test_generic_fixture import DataFixtureTestCase from django_dynamic_fixture.fixture_algorithms.unique_random_fixture import \ UniqueRandomDataFixture class RandomDataFixtureTestCase(TestCase, DataFixtureTestCase): def setUp(self): self.fixture = UniqueRandomDataFixture() def test_generated_strings_are_unique(self): results = set() for _ in range(self.fixture.OBJECT_COUNT): results.add( self.fixture.generate_data(models.CharField(max_length=10)) ) assert len(results) == self.fixture.OBJECT_COUNT def test_generated_signed_integers_are_unique(self): results = set() prev = 0 for _ in range(self.fixture.OBJECT_COUNT): integer = self.fixture.generate_data(models.IntegerField()) results.add(integer) assert abs(integer) > abs(prev) prev = integer assert len(results) == self.fixture.OBJECT_COUNT def test_generated_unsigned_integers_are_unique(self): results = set() prev = 0 for _ in range(self.fixture.OBJECT_COUNT): integer = self.fixture.generate_data(models.PositiveIntegerField()) results.add(integer) assert integer > prev prev = integer assert len(results) == self.fixture.OBJECT_COUNT def test_warning(self): with catch_warnings(record=True) as w: for _ in range(self.fixture.OBJECT_COUNT + 1): self.fixture.generate_data(models.CharField(max_length=10)) warning = w[-1] assert issubclass(warning.category, RuntimeWarning) expected_message = ( self.fixture.WARNING_MESSAGE_TMPL % self.fixture.OBJECT_COUNT ) assert expected_message in str(warning.message) django-dynamic-fixture-4.0.1/django_dynamic_fixture/fixture_algorithms/unique_random_fixture.py000066400000000000000000000132471450115615700335210ustar00rootroot00000000000000from datetime import datetime, date, timedelta from decimal import Decimal from itertools import chain import random import socket import string import struct from warnings import warn from django.core.exceptions import ImproperlyConfigured try: from django.utils.timezone import now except ImportError: now = datetime.now try: from django.contrib.gis.geos import * except ImproperlyConfigured: pass # environment without geo libs except Exception: pass # Avoid errors like GDALException from django_dynamic_fixture.fixture_algorithms.sequential_fixture import AutoDataFiller from django_dynamic_fixture.fixture_algorithms.default_fixture import BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin class UniqueRandomDataFixture(BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin): DEFAULT_LENGTH = 10 OBJECT_COUNT = 512 WARNING_MESSAGE_TMPL = ( 'Maximum number of objects (%d) is exceeded in ' 'unique_random_fixture. Uniqueness is not guaranteed.' ) def __init__(self): super().__init__() self.filler = AutoDataFiller() def get_counter(self, field, key): result = self.filler.next(key) if result > self.OBJECT_COUNT: warn(self.WARNING_MESSAGE_TMPL % self.OBJECT_COUNT, RuntimeWarning) return result def random_string(self, field, key, n=None): counter = str(self.get_counter(field, key)) length = n or self.DEFAULT_LENGTH result = counter result += ''.join( random.choice(string.ascii_letters) for _ in range(length - len(counter)) ) return result def random_integer(self, field, key, signed=True): counter = self.get_counter(field, key) - 1 counter %= self.OBJECT_COUNT if not signed: MAX_INT = 2 ** 16 multiplier = MAX_INT // self.OBJECT_COUNT return random.randrange( multiplier * counter + 1, multiplier * (counter + 1) ) MAX_SIGNED_INT = 2 ** 15 multiplier = MAX_SIGNED_INT // self.OBJECT_COUNT positive_range = range( multiplier * counter + 1, multiplier * (counter + 1) ) negative_range = range( (-multiplier) * (counter + 1), (-multiplier) * counter ) return random.choice(list(chain(positive_range, negative_range))) # NUMBERS def integerfield_config(self, field, key): return self.random_integer(field, key) def smallintegerfield_config(self, field, key): return self.random_integer(field, key) def bigintegerfield_config(self, field, key): return self.random_integer(field, key) def positiveintegerfield_config(self, field, key): return self.random_integer(field, key, signed=False) def positivesmallintegerfield_config(self, field, key): return self.random_integer(field, key, signed=False) def floatfield_config(self, field, key): return float(self.random_integer(field, key)) + random.random() def decimalfield_config(self, field, key): number_of_digits = field.max_digits - field.decimal_places max_value = 10 ** number_of_digits value = self.random_integer(field, key) % max_value value = float(value) + random.random() return Decimal(str(value)) # STRINGS def charfield_config(self, field, key): return self.random_string(field, key, field.max_length) def textfield_config(self, field, key): return self.charfield_config(field, key) def slugfield_config(self, field, key): return self.charfield_config(field, key) def commaseparatedintegerfield_config(self, field, key): return self.charfield_config(field, key) # BOOLEAN def booleanfield_config(self, field, key): counter = self.get_counter(field, key) if counter == 1: return True elif counter == 2: return False return random.choice((True, False)) def nullbooleanfield_config(self, field, key): counter = self.get_counter(field, key) if counter == 1: return None elif counter == 2: return True elif counter == 3: return False return random.choice((None, True, False)) # DATE/TIME RELATED def datefield_config(self, field, key): integer = self.random_integer(field, key, signed=False) return date.today() - timedelta(days=integer) def timefield_config(self, field, key): integer = self.random_integer(field, key, signed=False) return (now() - timedelta(seconds=integer)).time() def datetimefield_config(self, field, key): integer = self.random_integer(field, key, signed=False) return now() - timedelta(seconds=integer) # FORMATTED STRINGS def emailfield_config(self, field, key): return f'a{self.random_string(field, key)}@dynamicfixture.com' def urlfield_config(self, field, key): return f'http://dynamicfixture{self.random_string(field, key)}.com' # Deprecated in Django >= 1.7 def ipaddressfield_config(self, field, key): MAX_IP = 2 ** 32 - 1 integer = self.random_integer(field, key, signed=False) integer %= MAX_IP return str(socket.inet_ntoa(struct.pack('!L', integer))) def xmlfield_config(self, field, key): return f'{self.random_string(field, key)}' # FILES def filepathfield_config(self, field, key): return self.random_string(field, key) def filefield_config(self, field, key): return self.random_string(field, key) def imagefield_config(self, field, key): return self.random_string(field, key) django-dynamic-fixture-4.0.1/django_dynamic_fixture/global_settings.py000066400000000000000000000070671450115615700263510ustar00rootroot00000000000000 """ Module that contains wrappers and shortcuts. This is the facade of all features of DDF. """ import os import sys import warnings from django.conf import settings from django.urls import get_mod_func try: from importlib import import_module except ImportError: from django.utils.importlib import import_module from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, StaticSequentialDataFixture, GlobalSequentialDataFixture from django_dynamic_fixture.fixture_algorithms.random_fixture import RandomDataFixture class DDFImproperlyConfigured(Exception): "DDF is improperly configured. Some global settings has bad value in django settings." def get_ddf_config(name, default, cast=None, options=None, msg=''): try: value = os.getenv(name) # Priority for Env variables if not value: value = getattr(settings, name) if hasattr(settings, name) else default value = cast(value) if cast else value if options and value not in options: # to educate users to use the property correctly. raise DDFImproperlyConfigured() return value except Exception as e: raise DDFImproperlyConfigured(f'{name}="{value}": {msg} ({e})') def get_boolean_config(config_name, default=False): return get_ddf_config(config_name, default, options=[True, False], msg='it must be True or False') def get_data_fixture(default='sequential'): # It must be 'sequential', 'static_sequential', 'global_sequential', 'random' or 'path.to.CustomDataFixtureClass' try: INTERNAL_DATA_FIXTURES = {'sequential': SequentialDataFixture(), 'static_sequential': StaticSequentialDataFixture(), 'global_sequential': GlobalSequentialDataFixture(), 'random': RandomDataFixture()} if hasattr(settings, 'DDF_DEFAULT_DATA_FIXTURE'): if settings.DDF_DEFAULT_DATA_FIXTURE in INTERNAL_DATA_FIXTURES.keys(): return INTERNAL_DATA_FIXTURES[settings.DDF_DEFAULT_DATA_FIXTURE] else: # path.to.CustomDataFixtureClass mod_name, obj_name = get_mod_func(settings.DDF_DEFAULT_DATA_FIXTURE) module = import_module(mod_name) custom_data_fixture = getattr(module, obj_name) return custom_data_fixture() else: return INTERNAL_DATA_FIXTURES[default] except: raise DDFImproperlyConfigured( f"DDF_DEFAULT_DATA_FIXTURE ({settings.DDF_DEFAULT_DATA_FIXTURE}) must be " "'sequential', 'static_sequential', 'global_sequential', 'random' or 'path.to.CustomDataFixtureClass'." ) DDF_DEFAULT_DATA_FIXTURE = get_data_fixture(default='sequential') DDF_IGNORE_FIELDS = get_ddf_config('DDF_IGNORE_FIELDS', default=[], cast=list, msg='it must be a list of strings') DDF_FK_MIN_DEPTH = get_ddf_config('DDF_FK_MIN_DEPTH', default=0, cast=int, msg='it must be a integer number') if hasattr(settings, 'DDF_NUMBER_OF_LAPS'): warnings.warn( "The old DDF_NUMBER_OF_LAPS settings was replaced by the new DDF_FK_MIN_DEPTH.", DeprecationWarning ) DDF_FIELD_FIXTURES = get_ddf_config('DDF_FIELD_FIXTURES', default={}, cast=dict, msg='it must be a dict') DDF_DEFAULT_DATA_FIXTURE.plugins = DDF_FIELD_FIXTURES DDF_FILL_NULLABLE_FIELDS = get_boolean_config('DDF_FILL_NULLABLE_FIELDS', default=False) DDF_VALIDATE_MODELS = get_boolean_config('DDF_VALIDATE_MODELS', default=False) DDF_DEBUG_MODE = get_boolean_config('DDF_DEBUG_MODE', default=False) django-dynamic-fixture-4.0.1/django_dynamic_fixture/models.py000066400000000000000000000002501450115615700244370ustar00rootroot00000000000000from django.conf import settings import_models = getattr(settings, 'IMPORT_DDF_MODELS', False) if import_models: from django_dynamic_fixture.models_test import * django-dynamic-fixture-4.0.1/django_dynamic_fixture/models_sample_app.py000066400000000000000000000025141450115615700266450ustar00rootroot00000000000000from django.db import models class Publisher(models.Model): name = models.CharField(max_length=100, unique=True) class Author(models.Model): name = models.CharField(max_length=100) description = models.TextField(null=True) class Category(models.Model): name = models.CharField(max_length=100, unique=True) parent = models.ForeignKey('self', on_delete=models.DO_NOTHING, null=True, blank=True) class Book(models.Model): isb = models.CharField(max_length=100, unique=True) name = models.CharField(max_length=100) main_author = models.ForeignKey(Author, related_name='books', on_delete=models.DO_NOTHING) authors = models.ManyToManyField('Author', related_name='m2m') categories = models.ManyToManyField('Category', related_name='m2m') from .fields import JSONField metadata = JSONField(null=True) class BookPublisher(models.Model): book_edition = models.ForeignKey('BookEdition', on_delete=models.DO_NOTHING) publisher = models.ForeignKey('Publisher', on_delete=models.DO_NOTHING) comments = models.TextField(max_length=100) class BookEdition(models.Model): book = models.ForeignKey(Book, related_name='editions', on_delete=models.DO_NOTHING) publishers = models.ManyToManyField('Publisher', related_name='edition_publishers', through=BookPublisher) year = models.IntegerField() django-dynamic-fixture-4.0.1/django_dynamic_fixture/models_test.py000066400000000000000000000323441450115615700255070ustar00rootroot00000000000000#!/usr/bin/env python # https://docs.djangoproject.com/en/3.0/ref/models/fields import django from django.conf import settings from django.db import models from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django_dynamic_fixture.models_sample_app import * from django_dynamic_fixture.models_third_party import * class EmptyModel(models.Model): class Meta: app_label = 'django_dynamic_fixture' class ModelWithNumbers(models.Model): #id is a models.AutoField() integer = models.IntegerField(unique=True) smallinteger = models.SmallIntegerField(unique=True) positiveinteger = models.PositiveIntegerField(unique=True) positivesmallinteger = models.PositiveSmallIntegerField(unique=True) biginteger = models.BigIntegerField(unique=True) float = models.FloatField(unique=True) decimal = models.DecimalField(max_digits=2, decimal_places=1, unique=False) class Meta: verbose_name = 'Numbers' app_label = 'django_dynamic_fixture' class ModelWithStrings(models.Model): char = models.CharField(max_length=1, unique=True) string = models.CharField(max_length=50, unique=True) text = models.TextField(unique=True) slug = models.SlugField(unique=True) commaseparated = models.CommaSeparatedIntegerField(max_length=100, unique=True) class Meta: verbose_name = 'Strings' app_label = 'django_dynamic_fixture' class ModelWithBooleans(models.Model): # https://docs.djangoproject.com/en/1.6/ref/models/fields/#booleanfield # Django 1.6 changed the default value of BooleanField from False to None boolean = models.BooleanField(default=False) nullboolean = models.NullBooleanField() class Meta: verbose_name = 'Booleans' app_label = 'django_dynamic_fixture' class ModelWithDateTimes(models.Model): date = models.DateField(unique=True) datetime = models.DateTimeField(unique=True) time = models.TimeField(unique=True) class Meta: verbose_name = 'DateTimes' app_label = 'django_dynamic_fixture' class ModelWithBinary(models.Model): binary = models.BinaryField() class Meta: app_label = 'django_dynamic_fixture' class ModelWithFieldsWithCustomValidation(models.Model): email = models.EmailField(unique=True) url = models.URLField(unique=True) ip = models.IPAddressField(unique=False) ipv6 = models.GenericIPAddressField(unique=False) class Meta: verbose_name = 'Custom validation' app_label = 'django_dynamic_fixture' class ModelWithFileFields(models.Model): filepath = models.FilePathField(unique=True, blank=True) file = models.FileField(upload_to='.') try: import pil # just test it if the PIL package is installed image = models.ImageField(upload_to='.') except ImportError: pass class Meta: verbose_name = 'File fields' app_label = 'django_dynamic_fixture' class ModelWithDefaultValues(models.Model): integer_with_default = models.IntegerField(default=3) string_with_choices = models.CharField(max_length=5, choices=(('a', 'A'), ('b', 'B'))) string_with_choices_and_default = models.CharField(max_length=5, default='b', choices=(('a', 'A'), ('b', 'B'))) string_with_optgroup_choices = models.CharField(max_length=5, choices=(('group1', (('a', 'A'), ('b', 'B'))), ('group2', (('c', 'C'), ('d', 'D'))))) foreign_key_with_default = models.ForeignKey(EmptyModel, null=True, default=None, on_delete=models.DO_NOTHING) class Meta: verbose_name = 'Default values' app_label = 'django_dynamic_fixture' class ModelForNullable(models.Model): nullable = models.IntegerField(null=True) not_nullable = models.IntegerField(null=False) class Meta: verbose_name = 'Nullable' app_label = 'django_dynamic_fixture' class ModelForIgnoreList2(models.Model): nullable = models.IntegerField(null=True) non_nullable = models.IntegerField() class Meta: verbose_name = 'Ignore list 2' app_label = 'django_dynamic_fixture' class ModelForIgnoreList(models.Model): required = models.IntegerField(null=False) required_with_default = models.IntegerField(null=False, default=1) not_required = models.IntegerField(null=True) not_required_with_default = models.IntegerField(default=1) self_reference = models.ForeignKey('ModelForIgnoreList', on_delete=models.DO_NOTHING, null=True) different_reference = models.ForeignKey(ModelForIgnoreList2, on_delete=models.DO_NOTHING) class Meta: verbose_name = 'Ignore list' app_label = 'django_dynamic_fixture' class ModelRelated(models.Model): selfforeignkey = models.ForeignKey('self', on_delete=models.DO_NOTHING, null=True, blank=True) integer = models.IntegerField(null=True) integer_b = models.IntegerField(null=True) class Meta: verbose_name = 'Related' app_label = 'django_dynamic_fixture' class ModelRelatedThrough(models.Model): related = models.ForeignKey('ModelRelated', on_delete=models.DO_NOTHING) relationship = models.ForeignKey('ModelWithRelationships', on_delete=models.DO_NOTHING) class Meta: app_label = 'django_dynamic_fixture' def default_fk_value(): try: return ModelRelated.objects.get(id=1) except ModelRelated.DoesNotExist: ModelRelated.objects.create() return ModelRelated.objects.all()[0] def default_fk_id(): return default_fk_value().pk class ModelWithRelationships(models.Model): # relationship selfforeignkey = models.ForeignKey('self', on_delete=models.DO_NOTHING, null=True, blank=True) foreignkey = models.ForeignKey('ModelRelated', related_name='fk', on_delete=models.DO_NOTHING) onetoone = models.OneToOneField('ModelRelated', related_name='o2o', on_delete=models.DO_NOTHING) manytomany = models.ManyToManyField('ModelRelated', related_name='m2m') manytomany_through = models.ManyToManyField('ModelRelated', related_name='m2m_through', through=ModelRelatedThrough) foreignkey_with_default = models.ForeignKey('ModelRelated', related_name='fk2', default=default_fk_value, on_delete=models.DO_NOTHING) foreignkey_with_id_default = models.ForeignKey('ModelRelated', related_name='fk3', default=default_fk_id, on_delete=models.DO_NOTHING) integer = models.IntegerField(null=True) integer_b = models.IntegerField(null=True) # generic field # TODO class Meta: verbose_name = 'Relationships' app_label = 'django_dynamic_fixture' class ModelWithCyclicDependency(models.Model): model_b = models.ForeignKey('ModelWithCyclicDependency2', on_delete=models.DO_NOTHING, null=True) class Meta: verbose_name = 'Cyclic dependency' app_label = 'django_dynamic_fixture' class ModelWithCyclicDependency2(models.Model): model_a = models.ForeignKey(ModelWithCyclicDependency, on_delete=models.DO_NOTHING, null=True) class Meta: verbose_name = 'Cyclic dependency 2' app_label = 'django_dynamic_fixture' class ModelAbstract(models.Model): integer = models.IntegerField(unique=True) class Meta: abstract = True verbose_name = 'Abstract' app_label = 'django_dynamic_fixture' class ModelParent(ModelAbstract): class Meta: verbose_name = 'Parent' app_label = 'django_dynamic_fixture' class ModelChild(ModelParent): class Meta: verbose_name = 'Child' app_label = 'django_dynamic_fixture' class ModelChildWithCustomParentLink(ModelParent): my_custom_ref = models.OneToOneField(ModelParent, parent_link=True, related_name='my_custom_ref_x', on_delete=models.DO_NOTHING) class Meta: verbose_name = 'Custom child' app_label = 'django_dynamic_fixture' class ModelWithRefToParent(models.Model): parent = models.ForeignKey(ModelParent, on_delete=models.DO_NOTHING) class Meta: verbose_name = 'Child with parent' app_label = 'django_dynamic_fixture' class CustomDjangoField(models.IntegerField): pass class CustomDjangoField2(models.IntegerField): pass class CustomDjangoFieldMixin: pass class CustomDjangoFieldMultipleInheritance(CustomDjangoFieldMixin, models.IntegerField): pass class NewField(models.Field): # Avoid OperationalError("table has no column named ...") errors def db_type(self, connection): return 'char(25)' class ModelWithCustomFields(models.Model): x = CustomDjangoField(null=False) y = NewField(null=True) class Meta: verbose_name = 'Custom fields' app_label = 'django_dynamic_fixture' class ModelWithCustomFieldsMultipleInheritance(models.Model): x = CustomDjangoFieldMultipleInheritance(null=False) y = NewField(null=True) class Meta: verbose_name = 'Custom fields with multiple inheritance' app_label = 'django_dynamic_fixture' class ModelWithUnsupportedField(models.Model): z = NewField(null=False) class Meta: verbose_name = 'Unsupported field' app_label = 'django_dynamic_fixture' class ModelWithValidators(models.Model): field_validator = models.CharField(max_length=3, validators=[RegexValidator(regex=r'ok')]) clean_validator = models.CharField(max_length=3) class Meta: verbose_name = 'Validators' app_label = 'django_dynamic_fixture' def clean(self): if self.clean_validator != 'ok': raise ValidationError('ops') class ModelWithAutoDateTimes(models.Model): auto_now_add = models.DateField(auto_now_add=True) auto_now = models.DateField(auto_now=True) manytomany = models.ManyToManyField('ModelWithAutoDateTimes', related_name='m2m') class Meta: verbose_name = 'Auto DateTime' app_label = 'django_dynamic_fixture' class ModelForCopy2(models.Model): int_e = models.IntegerField() class Meta: verbose_name = 'Copy 2' app_label = 'django_dynamic_fixture' class ModelForCopy(models.Model): int_a = models.IntegerField() int_b = models.IntegerField(null=None) int_c = models.IntegerField() int_d = models.IntegerField() e = models.ForeignKey(ModelForCopy2, on_delete=models.DO_NOTHING) class Meta: verbose_name = 'Copy' app_label = 'django_dynamic_fixture' class ModelForLibrary2(models.Model): integer = models.IntegerField(null=True) integer_unique = models.IntegerField(unique=True) class Meta: verbose_name = 'Library 2' app_label = 'django_dynamic_fixture' class ModelForLibrary(models.Model): integer = models.IntegerField(null=True) integer_unique = models.IntegerField(unique=True) selfforeignkey = models.ForeignKey('self', on_delete=models.DO_NOTHING, null=True, blank=True) foreignkey = models.ForeignKey('ModelForLibrary2', related_name='fk', on_delete=models.DO_NOTHING) class Meta: verbose_name = 'Library' app_label = 'django_dynamic_fixture' class ProxyModelForLibrary(ModelForLibrary): class Meta: proxy = True verbose_name = 'Proxy Library' app_label = 'django_dynamic_fixture' class ModelWithUniqueCharField(models.Model): text_unique = models.CharField(max_length=20, unique=True) class Meta: verbose_name = 'Unique char field' app_label = 'django_dynamic_fixture' class ModelWithClean(models.Model): integer = models.IntegerField() class Meta: verbose_name = 'Clean' app_label = 'django_dynamic_fixture' def clean(self): if self.integer != 9999: # just for testing raise ValidationError('integer is not 9999') class ModelForSignals(models.Model): class Meta: verbose_name = 'Signals' app_label = 'django_dynamic_fixture' class ModelForSignals2(models.Model): class Meta: verbose_name = 'Signals 2' app_label = 'django_dynamic_fixture' class ModelForFieldPlugins(models.Model): # aaa = CustomDjangoField(null=False) # defined in settings.py # bbb = models.IntegerField(null=False) custom_field_custom_fixture = CustomDjangoField(null=False) # defined in settings.py custom_field_custom_fixture2 = CustomDjangoField2(null=False) # defined in settings.py class Meta: app_label = 'django_dynamic_fixture' class ModelWithCommonNames(models.Model): instance = models.IntegerField(null=False) field = models.IntegerField(null=False) class Meta: app_label = 'django_dynamic_fixture' class ModelWithNamedPrimaryKey(models.Model): named_pk = models.AutoField(primary_key=True) if (hasattr(settings, 'DDF_TEST_GEODJANGO') and settings.DDF_TEST_GEODJANGO): from django.contrib.gis.db import models as geomodels class ModelForGeoDjango(geomodels.Model): geometry = geomodels.GeometryField() point = geomodels.PointField() line_string = geomodels.LineStringField() polygon = geomodels.PolygonField() multi_point = geomodels.MultiPointField() multi_line_string = geomodels.MultiLineStringField() multi_polygon = geomodels.MultiPolygonField() geometry_collection = geomodels.GeometryCollectionField() class Meta: app_label = 'django_dynamic_fixture' class ModelForUUID(models.Model): uuid = models.UUIDField() class Meta: app_label = 'django_dynamic_fixture' django-dynamic-fixture-4.0.1/django_dynamic_fixture/models_third_party.py000066400000000000000000000027561450115615700270650ustar00rootroot00000000000000from django.db import models try: from django.contrib.postgres.fields import JSONField class ModelForPostgresFields(models.Model): nullable_json_field = JSONField(null=True) json_field = JSONField(null=False) class Meta: app_label = 'django_dynamic_fixture' except ImportError: pass try: from jsonfield import JSONField from jsonfield import JSONCharField class ModelForPlugins1(models.Model): json_field1 = JSONCharField(max_length=10) json_field2 = JSONField() class Meta: app_label = 'django_dynamic_fixture' except ImportError: print('Library `jsonfield` not installed. Skipping.') try: from json_field import JSONField as JSONField2 class ModelForPlugins2(models.Model): json_field1 = JSONField2() class Meta: app_label = 'django_dynamic_fixture' except ImportError: print('Library `django-json-field` not installed. Skipping.') try: from polymorphic.models import PolymorphicModel class ModelPolymorphic(PolymorphicModel): class Meta: verbose_name = 'Polymorphic Model' class ModelPolymorphic2(ModelPolymorphic): class Meta: verbose_name = 'Polymorphic Model 2' class ModelPolymorphic3(ModelPolymorphic): class CannotSave(Exception): pass def save(self): raise self.CannotSave except ImportError: print('Library `django_polymorphic` not installed. Skipping.') django-dynamic-fixture-4.0.1/django_dynamic_fixture/script_ddf_checkings.py000066400000000000000000000047251450115615700273260ustar00rootroot00000000000000import csv from django.db import transaction from django_dynamic_fixture.django_helper import get_apps, get_models_of_an_app def color(color, string): return '\033[1;{}m{}\033[0m'.format(color, string) def white(string): return color('37', string) def red(string): return color('91', string) def green(string): return color('92', string) def yellow(string): return color('93', string) def ddf_check_models(application_labels=[], exclude_application_labels=[], print_csv=False, csv_filename=None): from django_dynamic_fixture import get succeeded = {} errors = {} for app_label in get_apps(application_labels, exclude_application_labels): models = get_models_of_an_app(app_label) for model_class in models: ref = '{}.{}'.format(app_label, model_class.__name__) try: with transaction.atomic(): get(model_class) succeeded[ref] = None except Exception as e: errors[ref] = '[{}] {}'.format(type(e), str(e)) console_report(succeeded, errors) if print_csv or csv_filename: content = csv_report(succeeded, errors) if print_csv: print(yellow('\nCSV Report.\n')) print(content) if csv_filename: print(yellow('\nCSV Report file created: {}.\n'.format(csv_filename))) save_csv(content, filename=csv_filename) return succeeded, errors def console_report(succeeded, errors): print(green('\nModels that DDF can create using the default settings.\n')) for i, (ref, _) in enumerate(succeeded.items(), start=1): i = str(i).zfill(3) print(white('{}. {}: '.format(i, ref)) + green('succeeded')) print(red('\nModels that requires some customisation.\n')) for i, (ref, error) in enumerate(errors.items(), start=1): i = str(i).zfill(3) print(white('{}. {}: '.format(i, ref)) + red(error)) def csv_report(succeeded, errors): SEP = '\t' LN = '\n' lines = [] lines.append(SEP.join(['#', 'Model', 'Succeeded'])) for i, (ref, _) in enumerate(succeeded.items(), start=1): lines.append(SEP.join([str(i), ref, 'succeeded'])) lines.append(SEP.join(['#', 'Model', 'Error'])) for i, (ref, error) in enumerate(errors.items(), start=1): lines.append(SEP.join([str(i), ref, error])) return LN.join(lines) def save_csv(content, filename): with open(filename, 'w') as f: f.write(content) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/000077500000000000000000000000001450115615700237475ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/__init__.py000066400000000000000000000000011450115615700260470ustar00rootroot00000000000000 django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/conftest.py000066400000000000000000000010731450115615700261470ustar00rootroot00000000000000# PyTest file for global set up. # Django initialisation import django django.setup() # Give DB access to PyTests import pytest pytest.mark.django_db # MonkeyPatch Django-Test teardown from django.test import utils original_teardown_test_environment = utils.teardown_test_environment def fixed_teardown_test_environment(): try: original_teardown_test_environment() except TypeError: # Ignore some Django or PyTest-Django bug pass utils.teardown_test_environment = fixed_teardown_test_environment print(':: PyTest conftest.py loaded.') django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf.py000066400000000000000000000532451450115615700261260ustar00rootroot00000000000000from datetime import datetime, date from decimal import Decimal import uuid from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture data_fixture = SequentialDataFixture() class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(data_fixture) class NewCreateAModelInstanceTest(DDFTestCase): def test_new_create_a_non_saved_instance_of_the_model(self): instance = self.ddf.new(EmptyModel) assert isinstance(instance, EmptyModel) assert instance.id is None class GetDealWithPrimaryKeyTest(DDFTestCase): def test_get_use_database_id_by_default(self): instance = self.ddf.get(EmptyModel) assert instance.id is not None assert instance.pk is not None def test_get_use_given_id(self): instance = self.ddf.new(EmptyModel, id=99998) assert instance.id == 99998 assert instance.pk == 99998 def test_get_use_given_named_id(self): instance = self.ddf.get(ModelWithNamedPrimaryKey, named_pk=99998) assert instance.named_pk == 99998 assert instance.pk == 99998 class NewFullFillAttributesWithAutoDataTest(DDFTestCase): def test_new_fill_number_fields_with_numbers(self): instance = self.ddf.new(ModelWithNumbers) assert isinstance(instance.integer, int) assert isinstance(instance.smallinteger, int) assert isinstance(instance.positiveinteger, int) assert isinstance(instance.positivesmallinteger, int) assert isinstance(instance.biginteger, int) assert isinstance(instance.float, float) def test_new_fill_string_fields_with_text_type_strings(self): instance = self.ddf.new(ModelWithStrings) assert isinstance(instance.string, str) assert isinstance(instance.text, str) assert isinstance(instance.slug, str) assert isinstance(instance.commaseparated, str) def test_new_fill_boolean_fields_with_False_and_None(self): instance = self.ddf.new(ModelWithBooleans) assert instance.boolean is False assert instance.nullboolean is None def test_new_fill_time_related_fields_with_current_values(self): instance = self.ddf.new(ModelWithDateTimes) assert date.today() >= instance.date assert datetime.now().time() >= instance.time assert datetime.now() >= instance.datetime def test_new_fill_formatted_strings_fields_with_basic_values(self): instance = self.ddf.new(ModelWithFieldsWithCustomValidation) assert isinstance(instance.email, str) assert isinstance(instance.url, str) assert isinstance(instance.ip, str) assert isinstance(instance.ipv6, str) def test_new_fill_file_fields_with_basic_strings(self): instance = self.ddf.new(ModelWithFileFields) assert isinstance(instance.filepath, str) assert isinstance(instance.file.path, str) try: import pil # just test it if the PIL package is installed assert isinstance(instance.image, str) except ImportError: pass def test_new_fill_binary_fields_with_basic_data(self): value = b'\x00\x46\xFE' instance = self.ddf.new(ModelWithBinary, binary=value) assert bytes(instance.binary) == bytes(value) instance = self.ddf.get(ModelWithBinary) assert isinstance(instance.binary, bytes), type(instance.binary) class NewFullFillAttributesWithDefaultDataTest(DDFTestCase): def test_fill_field_with_default_data(self): instance = self.ddf.new(ModelWithDefaultValues) assert instance.integer_with_default == 3 def test_fill_field_with_possible_choices(self): instance = self.ddf.new(ModelWithDefaultValues) assert instance.string_with_choices == 'a' def test_fill_field_with_default_value_even_if_field_is_foreign_key(self): instance = self.ddf.new(ModelWithDefaultValues) assert instance.foreign_key_with_default is None def test_fill_field_with_default_data_and_choices_must_consider_default_data_instead_choices(self): instance = self.ddf.new(ModelWithDefaultValues) assert instance.string_with_choices_and_default == 'b' def test_fill_field_with_possible_optgroup_choices(self): instance = self.ddf.new(ModelWithDefaultValues) assert instance.string_with_optgroup_choices == 'a' class NewFullFillAttributesWithCustomDataTest(DDFTestCase): def test_fields_are_filled_with_custom_attributes(self): assert self.ddf.new(ModelWithNumbers, integer=9).integer == 9 assert self.ddf.new(ModelWithStrings, string='7').string == '7' assert self.ddf.new(ModelWithBooleans, boolean=True).boolean def test_decimal_can_be_filled_by_an_string(self): self.ddf.get(ModelWithNumbers, decimal='9.5') assert ModelWithNumbers.objects.latest('id').decimal == Decimal('9.5') def test_fields_can_be_filled_by_functions(self): instance = self.ddf.new(ModelWithStrings, string=lambda field: field.name) assert instance.string == 'string' def test_invalid_configuration_raise_an_error(self): with pytest.raises(InvalidConfigurationError): self.ddf.new(ModelWithNumbers, integer=lambda x: ''.invalidmethod()) def test_bad_data_raise_an_error(self): self.ddf.get(ModelWithNumbers, integer=50000) with pytest.raises(BadDataError): self.ddf.get(ModelWithNumbers, integer=50000) class NewIgnoringNullableFieldsTest(DDFTestCase): def test_new_do_not_fill_nullable_fields_if_we_do_not_want_to(self): self.ddf = DynamicFixture(data_fixture, fill_nullable_fields=False) instance = self.ddf.new(ModelForNullable) assert instance.not_nullable is not None assert instance.nullable is None class NewIgnoreFieldsInIgnoreListTest(DDFTestCase): def test_new_do_not_fill_ignored_fields(self): self.ddf = DynamicFixture(data_fixture, ignore_fields=['not_required', 'not_required_with_default']) instance = self.ddf.new(ModelForIgnoreList) assert instance.not_required is None assert instance.not_required_with_default is not None # not ignored fields assert instance.required is not None assert instance.required_with_default is not None def test_get_raise_an_error_if_a_required_field_is_in_ignore_list(self): self.ddf = DynamicFixture(data_fixture, ignore_fields=['required', 'required_with_default']) with pytest.raises(BadDataError): self.ddf.get(ModelForIgnoreList) def test_ignore_fields_are_propagated_to_self_references(self): self.ddf = DynamicFixture(data_fixture, ignore_fields=['not_required'], fk_min_depth=1, not_required=10) instance = self.ddf.new(ModelForIgnoreList) assert instance.not_required == 10 assert instance.self_reference is not None assert instance.self_reference.not_required is None def test_ignore_fields_are_not_propagated_to_different_references(self): self.ddf = DynamicFixture(data_fixture, ignore_fields=['non_nullable'], different_reference=DynamicFixture(data_fixture)) instance = self.ddf.new(ModelForIgnoreList) assert instance.different_reference is not None assert instance.different_reference.non_nullable is not None def test_ignore_fields_are_not_ignored_if_explicitely_given(self): self.ddf = DynamicFixture(data_fixture, not_required=3, ignore_fields=['not_required', 'nullable']) instance = self.ddf.new(ModelForIgnoreList) assert instance.not_required == 3 class NewAlsoCreatesRelatedObjectsTest(DDFTestCase): def test_new_fill_foreignkey_fields(self): instance = self.ddf.new(ModelWithRelationships) assert isinstance(instance.foreignkey, ModelRelated) def test_new_fill_onetoone_fields(self): instance = self.ddf.new(ModelWithRelationships) assert isinstance(instance.onetoone, ModelRelated) def test_new_deal_with_default_values(self): instance = self.ddf.new(ModelWithRelationships) assert isinstance(instance.foreignkey_with_default, ModelRelated), str(type(instance.foreignkey_with_default)) def test_new_deal_with_id_default_values(self): instance = self.ddf.new(ModelWithRelationships) assert isinstance(instance.foreignkey_with_id_default, ModelRelated), str(type(instance.foreignkey_with_default)) # TODO # def test_new_fill_genericrelations_fields(self): # instance = self.ddf.new(ModelWithRelationships) # assert isinstance(instance.foreignkey, ModelRelated) class NewCanCreatesCustomizedRelatedObjectsTest(DDFTestCase): def test_customizing_nullable_fields_for_related_objects(self): instance = self.ddf.new(ModelWithRelationships, selfforeignkey=DynamicFixture(data_fixture, fill_nullable_fields=True)) assert instance.integer is None assert isinstance(instance.selfforeignkey.integer, int) class NewDealWithSelfReferencesTest(DDFTestCase): def test_new_create_by_default_no_self_fks(self): instance = self.ddf.new(ModelWithRelationships, fill_nullable_fields=False) assert instance.selfforeignkey is None # no cycle instance = self.ddf.new(ModelWithRelationships, fill_nullable_fields=True) assert instance.selfforeignkey is None # no cycle def test_new_create_only_1_lap_in_cycle(self): self.ddf = DynamicFixture(data_fixture, fk_min_depth=1) instance = self.ddf.new(ModelWithRelationships) assert instance.selfforeignkey is not None # 1 cycle assert instance.selfforeignkey.selfforeignkey is None # 2 cycles def test_new_create_with_min_depth_2(self): self.ddf = DynamicFixture(data_fixture, fk_min_depth=2) instance = self.ddf.new(ModelWithRelationships) assert instance.selfforeignkey is not None # 1 cycle assert instance.selfforeignkey.selfforeignkey is not None # 2 cycles assert instance.selfforeignkey.selfforeignkey.selfforeignkey is None # 3 cycles def test_number_of_fk_cycles_does_not_break_default_non_null_fk(self): self.ddf = DynamicFixture(data_fixture, fk_min_depth=0) instance = self.ddf.new(ModelWithRefToParent) assert instance.parent is not None class GetFullFilledModelInstanceAndPersistTest(DDFTestCase): def test_get_create_and_save_a_full_filled_instance_of_the_model(self): instance = self.ddf.get(ModelWithRelationships) assert isinstance(instance, ModelWithRelationships) assert instance.id is not None # checking unique problems another_instance = self.ddf.get(ModelWithRelationships) assert isinstance(another_instance, ModelWithRelationships) assert another_instance.id is not None def test_get_create_and_save_related_fields(self): instance = self.ddf.get(ModelWithRelationships) assert instance.selfforeignkey is None assert instance.foreignkey is not None assert instance.onetoone is not None self.ddf = DynamicFixture(data_fixture, fk_min_depth=1) instance = self.ddf.get(ModelWithRelationships) assert instance.selfforeignkey is not None class ManyToManyRelationshipTest(DDFTestCase): def test_new_ignore_many_to_many_configuratios(self): instance = self.ddf.new(ModelWithRelationships, manytomany=3) instance.save() assert instance.manytomany.all().count() == 0 def test_get_ignore_many_to_many_configuratios(self): instance = self.ddf.get(ModelWithRelationships, manytomany=3) assert instance.manytomany.all().count() == 3 def test_many_to_many_configuratios_accept_list_of_dynamic_filters(self): instance = self.ddf.get(ModelWithRelationships, manytomany=[DynamicFixture(data_fixture, integer=1000), DynamicFixture(data_fixture, integer=1001)]) assert instance.manytomany.all().count() == 2 assert instance.manytomany.all()[0].integer == 1000 assert instance.manytomany.all()[1].integer == 1001 def test_many_to_many_configuratios_accept_list_of_instances(self): b1 = self.ddf.get(ModelRelated, integer=1000) b2 = self.ddf.get(ModelRelated, integer=1001) instance = self.ddf.get(ModelWithRelationships, manytomany=[b1, b2]) assert instance.manytomany.all().count() == 2 objs = instance.manytomany.all().order_by('integer') assert objs[0].integer == 1000 assert objs[1].integer == 1001 def test_invalid_many_to_many_configuration(self): with pytest.raises(InvalidManyToManyConfigurationError): self.ddf.get(ModelWithRelationships, manytomany='a') def test_many_to_many_through(self): b1 = self.ddf.get(ModelRelated, integer=1000) b2 = self.ddf.get(ModelRelated, integer=1001) instance = self.ddf.get(ModelWithRelationships, manytomany_through=[b1, b2]) objs = instance.manytomany_through.all().order_by('integer') assert objs.count() == 2 assert objs[0].integer == 1000 assert objs[1].integer == 1001 class NewDealWithCyclicDependenciesTest(DDFTestCase): def test_new_create_by_default_no_cycles(self): a = self.ddf.new(ModelWithCyclicDependency) assert a.model_b is None def test_new_create_only_1_lap_in_fk_cycle(self): self.ddf = DynamicFixture(data_fixture, fk_min_depth=1) a = self.ddf.get(ModelWithCyclicDependency) assert a.model_b.model_a is None def test_new_create_with_min_depth_2(self): self.ddf = DynamicFixture(data_fixture, fk_min_depth=2) a = self.ddf.get(ModelWithCyclicDependency) assert a.model_b.model_a.model_b is None class NewDealWithInheritanceTest(DDFTestCase): def test_get_must_raise_an_error_if_model_is_abstract(self): with pytest.raises(InvalidModelError): self.ddf.get(ModelAbstract) def test_get_must_fill_parent_fields_too(self): instance = self.ddf.get(ModelParent) assert isinstance(instance.integer, int) assert ModelParent.objects.count() == 1 def test_get_must_fill_grandparent_fields_too(self): instance = self.ddf.get(ModelChild) assert isinstance(instance.integer, int) assert ModelParent.objects.count() == 1 assert ModelChild.objects.count() == 1 def test_get_must_ignore_parent_link_attributes_but_the_parent_object_must_be_created(self): instance = self.ddf.get(ModelChildWithCustomParentLink) assert isinstance(instance.integer, int) assert ModelParent.objects.count() == 1 assert ModelChildWithCustomParentLink.objects.count() == 1 assert instance.my_custom_ref.id is not None assert instance.my_custom_ref.my_custom_ref_x.id is not None # TODO: need to check these tests. Here we are trying to simulate a bug with parent_link attribute def test_get_0(self): instance = self.ddf.get(ModelWithRefToParent) assert ModelWithRefToParent.objects.count() == 1 assert ModelParent.objects.count() == 1 assert isinstance(instance.parent, ModelParent) def test_get_1(self): instance = self.ddf.get(ModelWithRefToParent, parent=self.ddf.get(ModelChild)) assert ModelWithRefToParent.objects.count() == 1 assert ModelParent.objects.count() == 1 assert ModelChild.objects.count() == 1 assert isinstance(instance.parent, ModelChild) def test_get_2(self): instance = self.ddf.get(ModelWithRefToParent, parent=self.ddf.get(ModelChildWithCustomParentLink)) assert ModelWithRefToParent.objects.count() == 1 assert ModelParent.objects.count() == 1 assert ModelChildWithCustomParentLink.objects.count() == 1 assert isinstance(instance.parent, ModelChildWithCustomParentLink) class ComplexFieldsTest(DDFTestCase): def test_x(self): instance = self.ddf.new(ModelForUUID) assert isinstance(instance.uuid, uuid.UUID) class ModelValidatorsTest(DDFTestCase): def test_it_must_create_if_validation_is_disabled(self): instance = self.ddf.get(ModelWithValidators, field_validator='nok', clean_validator='nok') self.ddf.validate_models = False assert instance.field_validator == 'nok' assert instance.clean_validator == 'nok' def test_it_must_create_if_there_is_no_validation_errors(self): instance = self.ddf.get(ModelWithValidators, field_validator='ok', clean_validator='ok') self.ddf.validate_models = True assert instance.field_validator == 'ok' assert instance.clean_validator == 'ok' def test_it_must_raise_a_bad_data_error_if_data_is_not_valid(self): self.ddf.validate_models = True self.ddf.get(ModelWithValidators, field_validator='nok', clean_validator='ok') with pytest.raises(BadDataError): self.ddf.get(ModelWithValidators, field_validator='ok', clean_validator='nok') class ConfigurationValidatorTest(DDFTestCase): def test_it_must_raise_a_bad_data_error_if_data_is_not_valid(self): with pytest.raises(InvalidConfigurationError): self.ddf.get(EmptyModel, unexistent_field='x') class DisableAutoGeneratedDateTimesTest(DDFTestCase): def test_auto_generated_datetimes_must_be_respected_if_nothing_is_specified(self): instance = self.ddf.get(ModelWithAutoDateTimes) assert datetime.today().date() == instance.auto_now_add assert datetime.today().date() == instance.auto_now def test_it_must_ignore_auto_generated_datetime_if_a_custom_value_is_provided(self): instance = self.ddf.get(ModelWithAutoDateTimes, auto_now_add=date(2000, 12, 31)) assert instance.auto_now_add == date(2000, 12, 31) instance = self.ddf.get(ModelWithAutoDateTimes, auto_now=date(2000, 12, 31)) assert instance.auto_now == date(2000, 12, 31) def test_checking_if_implementation_works_for_m2m_fields_too(self): instance = self.ddf.get(ModelWithAutoDateTimes, manytomany=[DynamicFixture(data_fixture, auto_now_add=date(2000, 12, 31))]) assert instance.manytomany.all()[0].auto_now_add == date(2000, 12, 31) instance = self.ddf.get(ModelWithAutoDateTimes, manytomany=[DynamicFixture(data_fixture, auto_now=date(2000, 12, 31))]) assert instance.manytomany.all()[0].auto_now == date(2000, 12, 31) class ModelWithCustomValidationTest(DDFTestCase): def test_ddf_can_not_create_instance_of_models_with_custom_validations(self): self.ddf.validate_models = True with pytest.raises(BadDataError): self.ddf.get(ModelWithClean) self.ddf.get(ModelWithClean, integer=9999) # this does not raise an exception class ExceptionsLayoutMessagesTest(DDFTestCase): def test_UnsupportedFieldError(self): try: self.ddf.new(ModelWithUnsupportedField) self.fail() except UnsupportedFieldError as e: assert """django_dynamic_fixture.models_test.ModelWithUnsupportedField.z""" in str(e) def test_BadDataError(self): self.ddf = DynamicFixture(data_fixture, ignore_fields=['required', 'required_with_default']) try: self.ddf.get(ModelForIgnoreList) self.fail() except BadDataError as e: assert 'IntegrityError' in str(e), str(e) assert 'NULL' in str(e).upper(), str(e) def test_InvalidConfigurationError(self): try: self.ddf.new(ModelWithNumbers, integer=lambda x: ''.invalidmethod()) self.fail() except InvalidConfigurationError as e: assert 'django_dynamic_fixture.models_test.ModelWithNumbers.integer' in str(e) assert 'AttributeError' in str(e) assert 'invalidmethod' in str(e) def test_InvalidManyToManyConfigurationError(self): try: self.ddf.get(ModelWithRelationships, manytomany='a') self.fail() except InvalidManyToManyConfigurationError as e: assert """('Field: manytomany', 'a')""" == str(e) def test_InvalidModelError(self): try: self.ddf.get(ModelAbstract) self.fail() except InvalidModelError as e: assert """django_dynamic_fixture.models_test.ModelAbstract""" == str(e) def test_InvalidModelError_for_common_object(self): class MyClass: pass try: self.ddf.new(MyClass) self.fail() except InvalidModelError as e: assert """django_dynamic_fixture.tests.test_ddf.MyClass""" == str(e) class SanityTest(DDFTestCase): def test_create_lots_of_models_to_verify_data_unicity_errors(self): for i in range(1000): self.ddf.get(ModelWithNumbers) class AvoidNameCollisionTest(DDFTestCase): def test_avoid_common_name_instance(self): self.ddf = DynamicFixture(data_fixture, fill_nullable_fields=False) instance = self.ddf.new(ModelWithCommonNames) assert instance.instance is not None instance = self.ddf.new(ModelWithCommonNames, instance=3) assert instance.instance == 3 instance = self.ddf.get(ModelWithCommonNames) assert instance.instance is not None instance = self.ddf.get(ModelWithCommonNames, instance=4) assert instance.instance == 4 def test_avoid_common_name_field(self): self.ddf = DynamicFixture(data_fixture, fill_nullable_fields=False) instance = self.ddf.new(ModelWithCommonNames) assert instance.field is not None instance = self.ddf.new(ModelWithCommonNames, field=5) assert instance.field == 5 instance = self.ddf.get(ModelWithCommonNames) assert instance.field is not None instance = self.ddf.get(ModelWithCommonNames, field=6) assert instance.field == 6 django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_checkings.py000066400000000000000000000025021450115615700301320ustar00rootroot00000000000000 from django.test import TestCase from django_dynamic_fixture.ddf import DDFLibrary from django_dynamic_fixture import ddf_check_models, teach class DDFTestCase(TestCase): def setUp(self): DDFLibrary.get_instance().clear() class TestCheckCompatibility(DDFTestCase): def test_default(self): succeeded, errors = ddf_check_models() compatible_models = [ 'django_dynamic_fixture.EmptyModel', 'django_dynamic_fixture.ModelWithNumbers', ] for model in compatible_models: assert model in succeeded.keys(), model incompatible_models = [ 'django_dynamic_fixture.ModelWithUnsupportedField', ] for model in incompatible_models: assert model in errors.keys(), model # TODO: Consider the RelatedObjectDoesNotExist errors # https://stackoverflow.com/questions/26270042/how-do-you-catch-this-exception def test_teaching_ddf(self): teach('django_dynamic_fixture.ModelWithUnsupportedField', z='z') succeeded, errors = ddf_check_models() compatible_models = [ 'django_dynamic_fixture.ModelWithUnsupportedField', ] for model in compatible_models: assert model in succeeded.keys(), model assert model not in errors.keys(), model django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_copier.py000066400000000000000000000057631450115615700274710ustar00rootroot00000000000000from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture data_fixture = SequentialDataFixture() class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(data_fixture) class CopyTest(DDFTestCase): def test_it_should_copy_from_model_fields(self): instance = self.ddf.get(ModelForCopy, int_a=Copier('int_b'), int_b=3) assert instance.int_a == 3 def test_simple_scenario(self): instance = self.ddf.get(ModelForCopy, int_b=Copier('int_a')) assert instance.int_b == instance.int_a def test_order_of_attributes_must_be_superfluous(self): instance = self.ddf.get(ModelForCopy, int_a=Copier('int_b')) assert instance.int_a == instance.int_b def test_it_should_deal_with_multiple_copiers(self): instance = self.ddf.get(ModelForCopy, int_a=Copier('int_b'), int_c=Copier('int_d')) assert instance.int_a == instance.int_b assert instance.int_c == instance.int_d def test_multiple_copiers_can_depend_of_one_field(self): instance = self.ddf.get(ModelForCopy, int_a=Copier('int_c'), int_b=Copier('int_c')) assert instance.int_a == instance.int_c assert instance.int_b == instance.int_c def test_it_should_deal_with_dependent_copiers(self): instance = self.ddf.get(ModelForCopy, int_a=Copier('int_b'), int_b=Copier('int_c')) assert instance.int_a == instance.int_b assert instance.int_b == instance.int_c def test_it_should_deal_with_relationships(self): instance = self.ddf.get(ModelForCopy, int_a=Copier('e.int_e')) assert instance.int_a == instance.e.int_e instance = self.ddf.get(ModelForCopy, int_a=Copier('e.int_e'), e=DynamicFixture(data_fixture, int_e=5)) assert instance.int_a == 5 def test_it_should_raise_a_bad_data_error_if_value_is_invalid(self): with pytest.raises(BadDataError): self.ddf.get(ModelForCopy, int_a=Copier('int_b'), int_b=None) def test_it_should_raise_a_invalid_configuration_error_if_expression_is_bugged(self): with pytest.raises(InvalidConfigurationError): self.ddf.get(ModelForCopy, int_a=Copier('invalid_field')) with pytest.raises(InvalidConfigurationError): self.ddf.get(ModelForCopy, int_a=Copier('int_b.invalid_field')) def test_it_should_raise_a_invalid_configuration_error_if_copier_has_cyclic_dependency(self): with pytest.raises(InvalidConfigurationError): self.ddf.get(ModelForCopy, int_a=Copier('int_b'), int_b=Copier('int_a')) def test_it_must_copy_generated_data_mask_too(self): import re instance = self.ddf.get(ModelWithStrings, string=Mask('- _ #'), text=Copier('string')) assert re.match(r'[A-Z]{1} [a-z]{1} [0-9]{1}', instance.string) assert re.match(r'[A-Z]{1} [a-z]{1} [0-9]{1}', instance.text) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_custom_fields.py000066400000000000000000000120721450115615700310370ustar00rootroot00000000000000from django.conf import settings from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.decorators import only_for_database, POSTGRES from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture data_fixture = SequentialDataFixture() class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(data_fixture) class CustomFieldsTest(DDFTestCase): def test_new_field_that_extends_django_field_must_be_supported(self): instance = self.ddf.new(ModelWithCustomFields) assert instance.x == 1 def test_new_field_that_extends_django_field_must_be_supported_with_custom_value(self): instance = self.ddf.new(ModelWithCustomFields, x=6) assert instance.x == 6 def test_unsupported_field_is_filled_with_null_if_it_is_possible(self): instance = self.ddf.new(ModelWithCustomFields) assert instance.y is None def test_unsupported_field_raise_an_error_if_it_does_not_accept_null_value(self): DDFLibrary.get_instance().clear() with pytest.raises(UnsupportedFieldError): self.ddf.new(ModelWithUnsupportedField) def test_new_field_that_double_inherits_django_field_must_be_supported(self): instance = self.ddf.new(ModelWithCustomFieldsMultipleInheritance) assert instance.x == 1 def test_new_field_that_double_inherits_django_field_must_be_supported_with_custom_value(self): instance = self.ddf.new(ModelWithCustomFieldsMultipleInheritance, x=5) assert instance.x == 5 class NewFullFillAttributesUsingPluginsTest(DDFTestCase): def test_custom_field_not_registered_must_raise_an_unsupported_field_exception(self): DDFLibrary.get_instance().clear() with pytest.raises(UnsupportedFieldError): self.ddf.get(ModelWithUnsupportedField) def test_new_fill_field_with_data_generated_by_plugins_with_dict(self): data_fixture.plugins = settings.DDF_FIELD_FIXTURES try: instance = self.ddf.get(ModelForFieldPlugins) # assert instance.aaa == 123456789 # assert instance.bbb == 123456789 assert instance.custom_field_custom_fixture == 123456789 finally: data_fixture.plugins = {} def test_new_fill_field_with_data_generated_by_plugins_with_direct_fuction(self): data_fixture.plugins = settings.DDF_FIELD_FIXTURES try: instance = self.ddf.get(ModelForFieldPlugins) assert instance.custom_field_custom_fixture2 == 987654321 finally: data_fixture.plugins = {} # Real Custom Field def test_json_field_not_registered_must_raise_an_unsupported_field_exception(self): # jsonfield requires Django 1.4+ try: from jsonfield import JSONCharField, JSONField instance = self.ddf.new(ModelForPlugins1) assert False, 'JSON fields must not be supported by default' except ImportError: pass except UnsupportedFieldError as e: pass def test_new_fill_json_field_with_data_generated_by_plugins(self): # jsonfield requires Django 1.4+ try: import json from jsonfield import JSONCharField, JSONField data_fixture.plugins = settings.DDF_FIELD_FIXTURES try: instance = self.ddf.new(ModelForPlugins1) assert isinstance(instance.json_field1, str), type(instance.json_field1) assert isinstance(instance.json_field2, str), type(instance.json_field2) assert isinstance(json.loads(instance.json_field1), dict) assert isinstance(json.loads(instance.json_field2), list) assert instance.json_field1 == '{"some random value": "c"}' assert instance.json_field2 == '[1, 2, 3]' finally: data_fixture.plugins = {} except ImportError: pass class PostgresCustomFieldsTest(DDFTestCase): @only_for_database(POSTGRES) def test_json_field(self): instance = self.ddf.get(ModelForPostgresFields) assert instance.nullable_json_field == {} assert instance.json_field == {} @only_for_database(POSTGRES) def test_json_field_as_null(self): instance = self.ddf.get(ModelForPostgresFields, nullable_json_field=None) assert instance.nullable_json_field is None @only_for_database(POSTGRES) def test_json_field_as_dicts(self): instance = self.ddf.get(ModelForPostgresFields, json_field={'a': 1}) assert instance.json_field == {'a': 1} @only_for_database(POSTGRES) def test_json_field_as_lists(self): instance = self.ddf.get(ModelForPostgresFields, json_field=['str1', 2, {'c': 3}]) assert instance.json_field == ['str1', 2, {'c': 3}] @only_for_database(POSTGRES) def test_json_field_as_strings(self): instance = self.ddf.get(ModelForPostgresFields, json_field='str') assert instance.json_field == 'str' django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_custom_models.py000066400000000000000000000013641450115615700310560ustar00rootroot00000000000000from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture import G try: from polymorphic.models import PolymorphicModel class PolymorphicModelTest(TestCase): def test_create_polymorphic_model_and_retrieve(self): p = G(ModelPolymorphic) assert list(ModelPolymorphic.objects.all()) == [p] def test_create_polymorphic_model_2_and_retrieve(self): p = G(ModelPolymorphic2) assert list(ModelPolymorphic2.objects.all()) == [p] def test_cannot_save(self): with self.assertRaises(BadDataError): G(ModelPolymorphic3) except ImportError: pass django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_geo.py000066400000000000000000000027211450115615700267510ustar00rootroot00000000000000from django.conf import settings from django.core.exceptions import ImproperlyConfigured try: from django.contrib.gis.geos import * except ImproperlyConfigured: pass # environment without geo libs try: from django.contrib.gis.db import models as geomodel except ImproperlyConfigured: pass # environment without geo libs from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture data_fixture = SequentialDataFixture() class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(data_fixture) if (hasattr(settings, 'DDF_TEST_GEODJANGO') and settings.DDF_TEST_GEODJANGO): class GeoDjangoFieldsTest(DDFTestCase): def test_geodjango_fields(self): instance = self.ddf.new(ModelForGeoDjango) assert isinstance(instance.geometry, GEOSGeometry), str(type(instance.geometry)) assert isinstance(instance.point, Point) assert isinstance(instance.line_string, LineString) assert isinstance(instance.polygon, Polygon) assert isinstance(instance.multi_point, MultiPoint) assert isinstance(instance.multi_line_string, MultiLineString) assert isinstance(instance.multi_polygon, MultiPolygon) assert isinstance(instance.geometry_collection, GeometryCollection) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_signals.py000066400000000000000000000075161450115615700276460ustar00rootroot00000000000000from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.ddf import _PRE_SAVE, _POST_SAVE from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture data_fixture = SequentialDataFixture() class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(data_fixture) _PRE_SAVE.clear() _POST_SAVE.clear() class PreSaveTest(DDFTestCase): def test_set_pre_save_receiver(self): def callback_function(instance): pass set_pre_save_receiver(ModelForSignals, callback_function) callback_function = lambda x: x set_pre_save_receiver(ModelForSignals, callback_function) def test_pre_save_receiver_must_raise_an_error_if_first_parameter_is_not_a_model_class(self): callback_function = lambda x: x with pytest.raises(InvalidReceiverError): set_pre_save_receiver(str, callback_function) def test_pre_save_receiver_must_raise_an_error_if_it_is_not_a_function(self): with pytest.raises(InvalidReceiverError): set_pre_save_receiver(ModelForSignals, '') def test_pre_save_receiver_must_raise_an_error_if_it_is_not_an_only_one_argument_function(self): callback_function = lambda x, y: x with pytest.raises(InvalidReceiverError): set_pre_save_receiver(ModelForSignals, callback_function) def test_pre_save_receiver_must_be_executed_before_saving(self): def callback_function(instance): if instance.id is not None: raise Exception('ops, instance already saved') self.ddf.get(ModelForSignals2) set_pre_save_receiver(ModelForSignals, callback_function) self.ddf.get(ModelForSignals) assert ModelForSignals2.objects.count() == 1 def test_bugged_pre_save_receiver_must_raise_an_error(self): def callback_function(instance): raise Exception('ops') set_pre_save_receiver(ModelForSignals, callback_function) with pytest.raises(BadDataError): self.ddf.get(ModelForSignals) class PostSaveTest(DDFTestCase): def test_set_post_save_receiver(self): def callback_function(instance): pass set_post_save_receiver(ModelForSignals, callback_function) callback_function = lambda x: x set_post_save_receiver(ModelForSignals, callback_function) def test_post_save_receiver_must_raise_an_error_if_first_parameter_is_not_a_model_class(self): callback_function = lambda x: x with pytest.raises(InvalidReceiverError): set_post_save_receiver(str, callback_function) def test_post_save_receiver_must_raise_an_error_if_it_is_not_a_function(self): with pytest.raises(InvalidReceiverError): set_post_save_receiver(ModelForSignals, '') def test_post_save_receiver_must_raise_an_error_if_it_is_not_an_only_one_argument_function(self): callback_function = lambda x, y: x with pytest.raises(InvalidReceiverError): set_post_save_receiver(ModelForSignals, callback_function) def test_pre_save_receiver_must_be_executed_before_saving(self): def callback_function(instance): if instance.id is None: raise Exception('ops, instance not saved') self.ddf.get(ModelForSignals2) set_post_save_receiver(ModelForSignals, callback_function) self.ddf.get(ModelForSignals) assert ModelForSignals2.objects.count() == 1 def test_bugged_post_save_receiver_must_raise_an_error(self): def callback_function(instance): raise Exception('ops') set_post_save_receiver(ModelForSignals, callback_function) with pytest.raises(BadDataError): self.ddf.get(ModelForSignals) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_ddf_teaching_and_lessons.py000066400000000000000000000245101450115615700323510ustar00rootroot00000000000000import re from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture data_fixture = SequentialDataFixture() class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(data_fixture) DDFLibrary.get_instance().clear() class TeachAndLessonsTest(DDFTestCase): def test_teach_a_default_lesson_for_a_model(self): self.ddf.teach(ModelForLibrary, integer=1000) instance = self.ddf.get(ModelForLibrary) assert instance.integer == 1000 def test_default_lesson_may_be_overrided_although_it_is_an_anti_pattern(self): self.ddf.teach(ModelForLibrary, integer=1000) instance = self.ddf.get(ModelForLibrary) assert instance.integer == 1000 self.ddf.teach(ModelForLibrary, integer=1001) instance = self.ddf.get(ModelForLibrary) assert instance.integer == 1001 def test_it_must_NOT_raise_an_error_if_user_try_to_use_a_not_saved_default_configuration(self): self.ddf.get(ModelForLibrary) def test_it_must_raise_an_error_if_try_to_set_a_static_value_to_a_field_with_unicity(self): with pytest.raises(InvalidConfigurationError): self.ddf.teach(ModelForLibrary, integer_unique=1000) def test_it_allows_to_use_masks_as_lessons_for_unique_integer_fields(self): self.ddf.teach(ModelForLibrary, integer_unique=Mask('1###')) instance = self.ddf.get(ModelForLibrary) assert 1000 <= int(instance.integer_unique) <= 1999 def test_it_allows_to_use_masks_as_lessons_for_unique_char_fields(self): self.ddf.teach(ModelWithUniqueCharField, text_unique=Mask('---- ### __')) instance = self.ddf.get(ModelWithUniqueCharField) assert re.match(r'[A-Z]{4} [0-9]{3} [a-z]{2}', instance.text_unique) def test_it_must_accept_dynamic_values_for_fields_with_unicity(self): self.ddf.teach(ModelForLibrary, integer_unique=lambda field: 1000) def test_it_must_NOT_propagate_lessons_for_internal_dependencies(self): self.ddf.teach(ModelForLibrary, foreignkey=DynamicFixture(data_fixture, integer=1000)) instance = self.ddf.get(ModelForLibrary) assert instance.integer != 1000 assert instance.foreignkey.integer == 1000 def test_it_must_use_lessons_for_internal_dependencies(self): # ModelForLibrary.foreignkey is a `ModelForLibrary2` self.ddf.teach(ModelForLibrary, integer=1000) self.ddf.teach(ModelForLibrary2, integer=1001) instance = self.ddf.get(ModelForLibrary, foreignkey=DynamicFixture(data_fixture)) assert instance.integer == 1000 assert instance.foreignkey.integer == 1001 def test_it_uses_lessons_for_base_model_when_creating_a_proxy_model(self): self.ddf.teach(ModelForLibrary, integer=123) instance = self.ddf.get(ProxyModelForLibrary) assert instance.__class__ is ProxyModelForLibrary assert instance.integer == 123 def test_it_uses_lessons_for_proxy_models_when_creating_the_base_model(self): self.ddf.teach(ProxyModelForLibrary, integer=456) instance = self.ddf.get(ModelForLibrary) assert instance.__class__ is ModelForLibrary assert instance.integer == 456 def test_it_uses_lessons_for_proxy_models_when_creating_the_proxy_model(self): self.ddf.teach(ProxyModelForLibrary, integer=789) instance = self.ddf.get(ProxyModelForLibrary) assert instance.__class__ is ProxyModelForLibrary assert instance.integer == 789 # Not implemented yet # def test_teaching_must_store_ddf_configs_too(self): # self.ddf.teach(ModelForLibrary, fill_nullable_fields=False) # instance = self.ddf.get(ModelForLibrary) # assert instance.integer is None # DDFLibrary.get_instance().clear() # self.ddf.teach(ModelForLibrary, fill_nullable_fields=True) # instance = self.ddf.get(ModelForLibrary) # assert instance.integer is not None # Not implemented yet # def test_teaching_ddf_configs_must_NOT_be_propagated_to_another_models(self): # self.ddf.teach(ModelForLibrary, fill_nullable_fields=False) # instance = self.ddf.get(ModelForLibrary) # assert instance.integer is None # assert instance.foreignkey.integer is None # DDFLibrary.get_instance().clear() # self.ddf.teach(ModelForLibrary, fill_nullable_fields=True) # instance = self.ddf.get(ModelForLibrary) # assert instance.integer is not None # assert instance.foreignkey.integer is None # not populated class TeachingAndCustomLessonsTest(DDFTestCase): def test_a_model_can_have_custom_lessons(self): self.ddf.teach(ModelForLibrary, integer=1000, ddf_lesson=None) self.ddf.teach(ModelForLibrary, integer=1001, ddf_lesson='a name') instance = self.ddf.get(ModelForLibrary) assert instance.integer == 1000 instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name') assert instance.integer == 1001 def test_custom_lessons_must_not_be_used_if_not_explicity_specified(self): self.ddf.teach(ModelForLibrary, integer=1000, ddf_lesson='a name') instance = self.ddf.get(ModelForLibrary) assert instance.integer != 1000 def test_a_model_can_have_many_custom_lessons(self): self.ddf.teach(ModelForLibrary, integer=1000, ddf_lesson='a name') self.ddf.teach(ModelForLibrary, integer=1001, ddf_lesson='a name 2') instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name') assert instance.integer == 1000 instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name 2') assert instance.integer == 1001 def test_it_must_raise_an_error_if_user_try_to_use_a_not_saved_configuration(self): with pytest.raises(InvalidConfigurationError): self.ddf.get(ModelForLibrary, ddf_lesson='a not teached lesson') def test_default_lesson_and_custom_lesson_must_work_together(self): # regression test self.ddf.teach(ModelForLibrary, integer=1000, ddf_lesson='a name') self.ddf.teach(ModelForLibrary, integer=1001, ddf_lesson=True) self.ddf.teach(ModelForLibrary, integer=1002, ddf_lesson='a name2') instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name') assert instance.integer == 1000 instance = self.ddf.get(ModelForLibrary) assert instance.integer == 1001 instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name2') assert instance.integer == 1002 def test_default_lesson_and_custom_lesson_must_work_together_for_different_models(self): # regression test self.ddf.teach(ModelForLibrary, integer=1000, ddf_lesson='a name') self.ddf.teach(ModelForLibrary, integer=1001, ddf_lesson=True) self.ddf.teach(ModelForLibrary, integer=1002, ddf_lesson='a name2') self.ddf.teach(ModelForLibrary2, integer=2000, ddf_lesson='a name') self.ddf.teach(ModelForLibrary2, integer=2001, ddf_lesson=True) self.ddf.teach(ModelForLibrary2, integer=2002, ddf_lesson='a name2') instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name') assert instance.integer == 1000 instance = self.ddf.get(ModelForLibrary) assert instance.integer == 1001 instance = self.ddf.get(ModelForLibrary, ddf_lesson='a name2') assert instance.integer == 1002 instance = self.ddf.get(ModelForLibrary2, ddf_lesson='a name') assert instance.integer == 2000 instance = self.ddf.get(ModelForLibrary2) assert instance.integer == 2001 instance = self.ddf.get(ModelForLibrary2, ddf_lesson='a name2') assert instance.integer == 2002 class DDFLibraryTest(TestCase): def setUp(self): self.lib = DDFLibrary() def test_add_and_get_configuration_without_string_name(self): self.lib.add_configuration(ModelForLibrary, {'a': 1}) assert self.lib.get_configuration(ModelForLibrary) == {'a': 1} assert self.lib.get_configuration(ModelForLibrary, name=DDFLibrary.DEFAULT_KEY) == {'a': 1} assert self.lib.get_configuration(ModelForLibrary, name=None) == {'a': 1} self.lib.clear() self.lib.add_configuration(ModelForLibrary, {'a': 2}, name=None) assert self.lib.get_configuration(ModelForLibrary) == {'a': 2} assert self.lib.get_configuration(ModelForLibrary, name=DDFLibrary.DEFAULT_KEY) == {'a': 2} assert self.lib.get_configuration(ModelForLibrary, name=None) == {'a': 2} self.lib.clear() self.lib.add_configuration(ModelForLibrary, {'a': 3}, name=True) assert self.lib.get_configuration(ModelForLibrary) == {'a': 3} assert self.lib.get_configuration(ModelForLibrary, name=DDFLibrary.DEFAULT_KEY) == {'a': 3} assert self.lib.get_configuration(ModelForLibrary, name=None) == {'a': 3} def test_add_and_get_configuration_with_name(self): self.lib.add_configuration(ModelForLibrary, {'a': 1}, name='x') assert self.lib.get_configuration(ModelForLibrary, name='x') == {'a': 1} def test_clear_config(self): self.lib.clear_configuration(ModelForLibrary) # run ok if empty self.lib.add_configuration(ModelForLibrary, {'a': 1}) self.lib.add_configuration(ModelForLibrary, {'a': 2}, name='x') self.lib.add_configuration(ModelForLibrary2, {'a': 3}) self.lib.clear_configuration(ModelForLibrary) assert self.lib.get_configuration(ModelForLibrary) == {} with pytest.raises(Exception): self.lib.get_configuration(ModelForLibrary, name='x') assert self.lib.get_configuration(ModelForLibrary2) == {'a': 3} def test_clear(self): self.lib.add_configuration(ModelForLibrary, {'a': 1}) self.lib.add_configuration(ModelForLibrary, {'a': 2}, name='x') self.lib.add_configuration(ModelForLibrary2, {'a': 3}) self.lib.add_configuration(ModelForLibrary2, {'a': 4}, name='x') self.lib.clear() assert self.lib.get_configuration(ModelForLibrary) == {} with pytest.raises(Exception): self.lib.get_configuration(ModelForLibrary, name='x') assert self.lib.get_configuration(ModelForLibrary2) == {} with pytest.raises(Exception): self.lib.get_configuration(ModelForLibrary2, name='x') django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_decorators.py000066400000000000000000000030061450115615700275240ustar00rootroot00000000000000from unittest import TestCase from django.conf import settings from django_dynamic_fixture import decorators class SkipForDatabaseTest(TestCase): def setUp(self): self.it_was_executed = False def tearDown(self): # It is important to do not break others tests: global and shared variable decorators.DATABASE_ENGINE = settings.DATABASES['default']['ENGINE'] @decorators.only_for_database(decorators.POSTGRES) def method_postgres(self): self.it_was_executed = True def test_annotated_method_only_for_postgres(self): decorators.DATABASE_ENGINE = decorators.SQLITE3 self.method_postgres() assert self.it_was_executed is False decorators.DATABASE_ENGINE = decorators.POSTGRES self.method_postgres() assert self.it_was_executed class OnlyForDatabaseTest(TestCase): def setUp(self): self.it_was_executed = False def tearDown(self): # It is important to do not break others tests: global and shared variable decorators.DATABASE_ENGINE = settings.DATABASES['default']['ENGINE'] @decorators.skip_for_database(decorators.SQLITE3) def method_sqlite3(self): self.it_was_executed = True def test_annotated_method_skip_for_sqlite3(self): decorators.DATABASE_ENGINE = decorators.SQLITE3 self.method_sqlite3() assert self.it_was_executed is False decorators.DATABASE_ENGINE = decorators.POSTGRES self.method_sqlite3() assert self.it_was_executed django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_django_helper.py000066400000000000000000000251731450115615700301710ustar00rootroot00000000000000 from django.test import TestCase from django.db import models import pytest from django_dynamic_fixture import N, G from django_dynamic_fixture.models_test import * from django_dynamic_fixture.django_helper import * class DjangoHelperAppsTest(TestCase): def test_get_apps_must_return_all_installed_apps(self): assert len(get_apps()) >= 1 def test_get_apps_may_be_filtered_by_app_names(self): apps = get_apps(application_labels=['django_dynamic_fixture']) assert len(apps) == 1 def test_get_apps_may_ignore_some_apps(self): apps = len(get_apps(exclude_application_labels=['django_dynamic_fixture'])) assert len(get_apps()) - apps == 1 def test_app_name_must_be_valid(self): with pytest.raises(Exception): get_apps(application_labels=['x']) with pytest.raises(Exception): get_apps(exclude_application_labels=['x']) def test_get_app_name_must(self): import django_dynamic_fixture.models as ddf assert get_app_name(ddf) == 'django_dynamic_fixture' def test_get_models_of_an_app_must(self): ddf = get_apps(application_labels=['django_dynamic_fixture'])[0] models_ddf = get_models_of_an_app(ddf) assert len(models_ddf) > 0 assert ModelWithNumbers in models_ddf class DjangoHelperModelsTest(TestCase): def test_get_model_name(self): class MyModel_test_get_model_name(models.Model): pass assert get_model_name(MyModel_test_get_model_name) == 'MyModel_test_get_model_name' def test_get_unique_model_name(self): class MyModel_test_get_unique_model_name(models.Model): pass assert get_unique_model_name(MyModel_test_get_unique_model_name) == 'django_dynamic_fixture.tests.test_django_helper.MyModel_test_get_unique_model_name' def test_get_fields_from_model(self): class Model4GetFields_test_get_fields_from_model(models.Model): integer = models.IntegerField() fields = get_fields_from_model(Model4GetFields_test_get_fields_from_model) assert get_field_by_name_or_raise(Model4GetFields_test_get_fields_from_model, 'id') in fields assert get_field_by_name_or_raise(Model4GetFields_test_get_fields_from_model, 'integer') in fields def test_get_local_fields(self): class ModelForGetLocalFields_test_get_local_fields(models.Model): integer = models.IntegerField() fields = get_local_fields(ModelForGetLocalFields_test_get_local_fields) assert get_field_by_name_or_raise(ModelForGetLocalFields_test_get_local_fields, 'id') in fields assert get_field_by_name_or_raise(ModelForGetLocalFields_test_get_local_fields, 'integer') in fields def test_get_field_names_of_model(self): class Model4GetFieldNames_test_get_field_names_of_model(models.Model): smallinteger = models.SmallIntegerField() fields = get_field_names_of_model(Model4GetFieldNames_test_get_field_names_of_model) assert 'smallinteger' in fields assert 'unknown' not in fields def test_get_many_to_many_fields_from_model(self): class ModelRelated_test_get_many_to_many_fields_from_model(models.Model): pass class ModelWithM2M_test_get_many_to_many_fields_from_model(models.Model): manytomany = models.ManyToManyField('ModelRelated_test_get_many_to_many_fields_from_model', related_name='m2m') fields = get_many_to_many_fields_from_model(ModelWithM2M_test_get_many_to_many_fields_from_model) assert get_field_by_name_or_raise(ModelWithM2M_test_get_many_to_many_fields_from_model, 'manytomany') in fields assert get_field_by_name_or_raise(ModelWithM2M_test_get_many_to_many_fields_from_model, 'id') not in fields def test_is_model_class(self): class MyModel_test_is_model_class(models.Model): pass assert is_model_class(MyModel_test_is_model_class) == True class X(object): pass assert is_model_class(X) == False def test_is_model_abstract(self): class AbstractModel_test_is_model_abstract(models.Model): class Meta: abstract = True assert is_model_abstract(AbstractModel_test_is_model_abstract) class ConcreteModel_test_is_model_abstract(models.Model): class Meta: abstract = False assert is_model_abstract(ConcreteModel_test_is_model_abstract) == False def test_is_model_managed(self): class NotManagedModel_test_is_model_managed(models.Model): class Meta: managed = False assert is_model_managed(NotManagedModel_test_is_model_managed) == False class ManagedModel_test_is_model_managed(models.Model): class Meta: managed = True assert is_model_managed(ManagedModel_test_is_model_managed) def test_model_has_the_field(self): class ModelWithWithoutFields_test_model_has_the_field(models.Model): integer = models.IntegerField() selfforeignkey = models.ForeignKey('self', null=True, on_delete=models.DO_NOTHING) manytomany = models.ManyToManyField('self', related_name='m2m') assert model_has_the_field(ModelWithWithoutFields_test_model_has_the_field, 'integer') assert model_has_the_field(ModelWithWithoutFields_test_model_has_the_field, 'selfforeignkey') assert model_has_the_field(ModelWithWithoutFields_test_model_has_the_field, 'manytomany') assert model_has_the_field(ModelWithWithoutFields_test_model_has_the_field, 'x') == False class DjangoHelperFieldsTest(TestCase): def test_get_unique_field_name(self): class Model4GetUniqueFieldName_test_get_unique_field_name(models.Model): integer = models.IntegerField() field = get_field_by_name_or_raise(Model4GetUniqueFieldName_test_get_unique_field_name, 'integer') assert get_unique_field_name(field) == 'django_dynamic_fixture.tests.test_django_helper.Model4GetUniqueFieldName_test_get_unique_field_name.integer' def test_get_related_model(self): class ModelRelated_test_get_related_model(models.Model): pass class Model4GetRelatedModel_test_get_related_model(models.Model): fk = models.ForeignKey(ModelRelated_test_get_related_model, on_delete=models.DO_NOTHING) assert get_related_model(get_field_by_name_or_raise(Model4GetRelatedModel_test_get_related_model, 'fk')) == \ ModelRelated_test_get_related_model def test_field_is_a_parent_link(self): class ModelParent_test_get_related_model(models.Model): pass class Model4FieldIsParentLink_test_get_related_model(ModelParent): o2o_with_parent_link = models.OneToOneField(ModelParent_test_get_related_model, parent_link=True, related_name='my_custom_ref_x', on_delete=models.DO_NOTHING) class Model4FieldIsParentLink2(ModelParent): o2o_without_parent_link = models.OneToOneField(ModelParent_test_get_related_model, parent_link=False, related_name='my_custom_ref_y', on_delete=models.DO_NOTHING) # FIXME # assert field_is_a_parent_link(get_field_by_name_or_raise(Model4FieldIsParentLink, 'o2o_with_parent_link')) assert field_is_a_parent_link(get_field_by_name_or_raise(Model4FieldIsParentLink2, 'o2o_without_parent_link')) == False def test_field_has_choices(self): class Model4FieldHasChoices_test_get_related_model(models.Model): with_choices = models.IntegerField(choices=((1, 1), (2, 2))) without_choices = models.IntegerField() assert field_has_choices(get_field_by_name_or_raise(Model4FieldHasChoices_test_get_related_model, 'with_choices')) assert field_has_choices(get_field_by_name_or_raise(Model4FieldHasChoices_test_get_related_model, 'without_choices')) == False def test_field_has_default_value(self): class Model4FieldHasDefault_test_field_has_default_value(models.Model): with_default = models.IntegerField(default=1) without_default = models.IntegerField() assert field_has_default_value(get_field_by_name_or_raise(Model4FieldHasDefault_test_field_has_default_value, 'with_default')) assert field_has_default_value(get_field_by_name_or_raise(Model4FieldHasDefault_test_field_has_default_value, 'without_default')) == False def test_field_is_unique(self): class Model4FieldMustBeUnique_test_field_is_unique(models.Model): unique = models.IntegerField(unique=True) not_unique = models.IntegerField() assert field_is_unique(get_field_by_name_or_raise(Model4FieldMustBeUnique_test_field_is_unique, 'unique')) assert field_is_unique(get_field_by_name_or_raise(Model4FieldMustBeUnique_test_field_is_unique, 'not_unique')) == False def test_is_key_field(self): class ModelForKeyField_test_is_key_field(models.Model): integer = models.IntegerField() assert is_key_field(get_field_by_name_or_raise(ModelForKeyField_test_is_key_field, 'id')) assert is_key_field(get_field_by_name_or_raise(ModelForKeyField_test_is_key_field, 'integer')) == False def test_is_relationship_field(self): class ModelForRelationshipField_test_is_relationship_field(models.Model): fk = models.ForeignKey('self', on_delete=models.DO_NOTHING) one2one = models.OneToOneField('self', on_delete=models.DO_NOTHING) assert is_relationship_field(get_field_by_name_or_raise(ModelForRelationshipField_test_is_relationship_field, 'fk')) assert is_relationship_field(get_field_by_name_or_raise(ModelForRelationshipField_test_is_relationship_field, 'one2one')) assert is_relationship_field(get_field_by_name_or_raise(ModelForRelationshipField_test_is_relationship_field, 'id')) == False def test_is_file_field(self): class ModelForFileField_test_is_file_field(models.Model): filefield = models.FileField() assert is_file_field(get_field_by_name_or_raise(ModelForFileField_test_is_file_field, 'filefield')) assert is_file_field(get_field_by_name_or_raise(ModelForFileField_test_is_file_field, 'id')) == False class PrintFieldValuesTest(TestCase): def test_model_not_saved_do_not_raise_an_exception(self): instance = N(ModelWithNumbers) print_field_values(instance) def test_model_saved_do_not_raise_an_exception(self): instance = G(ModelWithNumbers) print_field_values(instance) def test_print_accept_list_of_models_too(self): instances = G(ModelWithNumbers, n=2) print_field_values(instances) print_field_values([G(ModelWithNumbers), G(ModelWithNumbers)]) def test_print_accept_a_queryset_too(self): G(ModelWithNumbers, n=2) print_field_values(ModelWithNumbers.objects.all()) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_fdf.py000066400000000000000000000167511450115615700261310ustar00rootroot00000000000000from django_dynamic_fixture.fdf import * from django.core.files import File class FileSystemDjangoTestCaseDealWithDirectoriesTest(FileSystemDjangoTestCase): def test_create_temp_directory_must_create_an_empty_directory(self): directory = self.create_temp_directory() self.assertDirectoryExists(directory) self.assertNumberOfFiles(directory, 0) def test_remove_temp_directory(self): directory = self.create_temp_directory() self.remove_temp_directory(directory) self.assertDirectoryDoesNotExists(directory) def test_remove_temp_directory_must_remove_directories_created_out_of_the_testcase_too(self): directory = tempfile.mkdtemp() self.remove_temp_directory(directory) self.assertDirectoryDoesNotExists(directory) def test_remove_temp_directory_must_remove_their_files_too(self): directory = self.create_temp_directory() self.create_temp_file_with_name(directory, 'x.txt') self.remove_temp_directory(directory) self.assertDirectoryDoesNotExists(directory) class FileSystemDjangoTestCaseDealWithTemporaryFilesTest(FileSystemDjangoTestCase): def test_create_temp_file_must_create_a_temporary_file_in_an_arbitrary_directory(self): filepath = self.create_temp_file() self.assertFileExists(filepath) directory = self.get_directory_of_the_file(filepath) filename = self.get_filename(filepath) self.assertDirectoryContainsFile(directory, filename) def test_create_temp_file_must_create_an_empty_temporary_file(self): filepath = self.create_temp_file() assert self.get_content_of_file(filepath) == '' def test_create_temp_file_must_is_ready_to_add_content_to_it(self): filepath = self.create_temp_file() self.add_text_to_file(filepath, 'abc') assert self.get_content_of_file(filepath) == 'abc' class FileSystemDjangoTestCaseDealWithSpecificTemporaryFilesTest(FileSystemDjangoTestCase): def test_create_temp_file_with_name_must_create_a_file_in_an_specific_directory_with_an_specific_name(self): directory = self.create_temp_directory() filepath = self.create_temp_file_with_name(directory, 'x.txt') self.assertFileExists(filepath) assert self.get_directory_of_the_file(filepath) == directory assert self.get_filename(filepath) == 'x.txt' self.assertDirectoryContainsFile(directory, self.get_filename(filepath)) self.assertNumberOfFiles(directory, 1) def test_create_temp_file_with_name_must_create_an_empty_file(self): directory = self.create_temp_directory() filepath = self.create_temp_file_with_name(directory, 'x.txt') assert self.get_content_of_file(filepath) == '' def test_create_temp_file_with_name_is_ready_to_add_content_to_it(self): directory = self.create_temp_directory() filepath = self.create_temp_file_with_name(directory, 'x.txt') self.add_text_to_file(filepath, 'abc') assert self.get_content_of_file(filepath) == 'abc' class FileSystemDjangoTestCaseCanRenameFilesTest(FileSystemDjangoTestCase): def test_rename_file_must_preserve_the_directory(self): directory = self.create_temp_directory() old_filepath = self.create_temp_file_with_name(directory, 'x.txt') old_filename = self.get_filename(old_filepath) new_filepath = self.rename_temp_file(old_filepath, 'y.txt') self.assertFileExists(new_filepath) self.assertFileDoesNotExists(old_filepath) new_filename = self.get_filename(new_filepath) self.assertDirectoryContainsFile(directory, new_filename) self.assertDirectoryDoesNotContainsFile(directory, old_filename) def test_rename_file_must_preserve_the_file_content(self): directory = self.create_temp_directory() old_filepath = self.create_temp_file_with_name(directory, 'x.txt') self.add_text_to_file(old_filepath, 'abc') new_filepath = self.rename_temp_file(old_filepath, 'y.txt') assert self.get_content_of_file(new_filepath) == 'abc' class FileSystemDjangoTestCaseCanRemoveFilesTest(FileSystemDjangoTestCase): def test_remove_file(self): filepath = self.create_temp_file() self.remove_temp_file(filepath) self.assertFileDoesNotExists(filepath) def test_remove_file_must_remove_files_with_custom_name_too(self): directory = self.create_temp_directory() filepath = self.create_temp_file_with_name(directory, 'x.txt') self.remove_temp_file(filepath) self.assertFileDoesNotExists(filepath) def test_remove_file_must_remove_files_with_data_too(self): filepath = self.create_temp_file() self.add_text_to_file(filepath, 'abc') self.remove_temp_file(filepath) self.assertFileDoesNotExists(filepath) class FileSystemDjangoTestCaseCanCopyFilesTest(FileSystemDjangoTestCase): def test_copy_file_to_dir_must_not_remove_original_file(self): directory = self.create_temp_directory() filepath = self.create_temp_file() new_filepath = self.copy_file_to_dir(filepath, directory) self.assertFileExists(new_filepath) self.assertFileExists(filepath) def test_copy_file_to_dir_must_preserve_file_content(self): directory = self.create_temp_directory() filepath = self.create_temp_file() self.add_text_to_file(filepath, 'abc') new_filepath = self.copy_file_to_dir(filepath, directory) self.get_content_of_file(new_filepath) == 'abc' class FileSystemDjangoTestCaseDealWithDjangoFileFieldTest(FileSystemDjangoTestCase): def test_django_file_must_create_a_django_file_object(self): django_file = self.create_django_file_with_temp_file('x.txt') assert isinstance(django_file, File) assert django_file.name == 'x.txt' def test_django_file_must_create_a_temporary_file_ready_to_add_content(self): django_file = self.create_django_file_with_temp_file('x.txt') filepath = django_file.file.name self.add_text_to_file(filepath, 'abc') assert self.get_content_of_file(filepath) == 'abc' class FileSystemDjangoTestCaseTearDownTest(FileSystemDjangoTestCase): def test_teardown_must_delete_all_created_files_in_tests(self): directory = self.create_temp_directory() filepath1 = self.create_temp_file() filepath2 = self.create_temp_file_with_name(directory, 'x.txt') self.fdf_teardown() self.assertFileDoesNotExists(filepath1) self.assertFileDoesNotExists(filepath2) def test_teardown_must_delete_files_with_content_too(self): filepath = self.create_temp_file() self.add_text_to_file(filepath, 'abc') self.fdf_teardown() self.assertFileDoesNotExists(filepath) def test_teardown_must_delete_all_created_directories_in_tests(self): directory = self.create_temp_directory() self.fdf_teardown() self.assertDirectoryDoesNotExists(directory) class FileSystemDjangoTestCaseTearDownFrameworkConfigurationTest(FileSystemDjangoTestCase): def tearDown(self): super().tearDown() self.assertFileDoesNotExists(self.filepath1) self.assertFileDoesNotExists(self.filepath2) self.assertDirectoryDoesNotExists(self.directory) def test_creating_directory_and_files_for_the_testcase(self): self.directory = self.create_temp_directory() self.filepath1 = self.create_temp_file() self.filepath2 = self.create_temp_file_with_name(self.directory, 'x.txt') django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_global_settings.py000066400000000000000000000115001450115615700305350ustar00rootroot00000000000000import importlib from django import conf from django.test import TestCase import pytest from django_dynamic_fixture import global_settings from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture, \ StaticSequentialDataFixture from django_dynamic_fixture.fixture_algorithms.random_fixture import RandomDataFixture class AbstractGlobalSettingsTestCase(TestCase): def tearDown(self): if hasattr(conf.settings, 'DDF_DEFAULT_DATA_FIXTURE'): del conf.settings.DDF_DEFAULT_DATA_FIXTURE if hasattr(conf.settings, 'DDF_FILL_NULLABLE_FIELDS'): del conf.settings.DDF_FILL_NULLABLE_FIELDS if hasattr(conf.settings, 'DDF_IGNORE_FIELDS'): del conf.settings.DDF_IGNORE_FIELDS if hasattr(conf.settings, 'DDF_NUMBER_OF_LAPS'): del conf.settings.DDF_NUMBER_OF_LAPS if hasattr(conf.settings, 'DDF_VALIDATE_MODELS'): del conf.settings.DDF_VALIDATE_MODELS importlib.reload(conf) class CustomDataFixture: pass class DDF_DEFAULT_DATA_FIXTURE_TestCase(AbstractGlobalSettingsTestCase): def test_not_configured_must_load_default_value(self): importlib.reload(global_settings) assert SequentialDataFixture == type(global_settings.DDF_DEFAULT_DATA_FIXTURE) def test_may_be_an_internal_data_fixture_nick_name(self): conf.settings.DDF_DEFAULT_DATA_FIXTURE = 'sequential' importlib.reload(global_settings) assert SequentialDataFixture == type(global_settings.DDF_DEFAULT_DATA_FIXTURE) conf.settings.DDF_DEFAULT_DATA_FIXTURE = 'random' importlib.reload(global_settings) assert RandomDataFixture == type(global_settings.DDF_DEFAULT_DATA_FIXTURE) conf.settings.DDF_DEFAULT_DATA_FIXTURE = 'static_sequential' importlib.reload(global_settings) assert StaticSequentialDataFixture == type(global_settings.DDF_DEFAULT_DATA_FIXTURE) def test_may_be_a_path_to_a_custom_data_fixture(self): conf.settings.DDF_DEFAULT_DATA_FIXTURE = 'django_dynamic_fixture.tests.test_global_settings.CustomDataFixture' importlib.reload(global_settings) assert CustomDataFixture == type(global_settings.DDF_DEFAULT_DATA_FIXTURE) def test_if_path_can_not_be_found_it_will_raise_an_exception(self): conf.settings.DDF_DEFAULT_DATA_FIXTURE = 'unknown_path.CustomDataFixture' with pytest.raises(Exception): importlib.reload(global_settings) class DDF_FILL_NULLABLE_FIELDS_TestCase(AbstractGlobalSettingsTestCase): def test_not_configured_must_load_default_value(self): importlib.reload(global_settings) assert global_settings.DDF_FILL_NULLABLE_FIELDS is False def test_must_be_a_boolean(self): conf.settings.DDF_FILL_NULLABLE_FIELDS = True importlib.reload(global_settings) assert global_settings.DDF_FILL_NULLABLE_FIELDS def test_must_raise_an_exception_if_it_is_not_a_boolean(self): conf.settings.DDF_FILL_NULLABLE_FIELDS = 'x' with pytest.raises(Exception): importlib.reload(global_settings) class DDF_IGNORE_FIELDS_TestCase(AbstractGlobalSettingsTestCase): def test_not_configured_must_load_default_value(self): importlib.reload(global_settings) assert global_settings.DDF_IGNORE_FIELDS == [] def test_must_be_a_list_of_strings(self): conf.settings.DDF_IGNORE_FIELDS = ['x'] importlib.reload(global_settings) assert global_settings.DDF_IGNORE_FIELDS == ['x'] def test_must_raise_an_exception_if_it_is_not_an_list_of_strings(self): conf.settings.DDF_IGNORE_FIELDS = None with pytest.raises(Exception): importlib.reload(global_settings) class DDF_FK_MIN_DEPTH_TestCase(AbstractGlobalSettingsTestCase): def test_not_configured_must_load_default_value(self): importlib.reload(global_settings) assert global_settings.DDF_FK_MIN_DEPTH == 0 def test_must_be_an_integer(self): conf.settings.DDF_FK_MIN_DEPTH = 2 importlib.reload(global_settings) assert global_settings.DDF_FK_MIN_DEPTH == 2 def test_must_raise_an_exception_if_it_is_not_an_integer(self): conf.settings.DDF_FK_MIN_DEPTH = None with pytest.raises(Exception): importlib.reload(global_settings) class DDF_VALIDATE_MODELS_TestCase(AbstractGlobalSettingsTestCase): def test_not_configured_must_load_default_value(self): importlib.reload(global_settings) assert global_settings.DDF_VALIDATE_MODELS is False def test_must_be_a_boolean(self): conf.settings.DDF_VALIDATE_MODELS = False importlib.reload(global_settings) assert global_settings.DDF_VALIDATE_MODELS is False def test_must_raise_an_exception_if_it_is_not_a_boolean(self): conf.settings.DDF_VALIDATE_MODELS = 'x' with pytest.raises(Exception): importlib.reload(global_settings) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_mask.py000066400000000000000000000032221450115615700263120ustar00rootroot00000000000000import re from django.test import TestCase import pytest from django_dynamic_fixture.models_test import * from django_dynamic_fixture.ddf import * from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture class DDFTestCase(TestCase): def setUp(self): self.ddf = DynamicFixture(SequentialDataFixture()) class MaskTests(DDFTestCase): def test_it_must_generate_random_numbers(self): instance = self.ddf.get(ModelWithStrings, string=Mask('###')) assert re.match(r'\d{3}', instance.string) def test_it_must_generate_lower_ascii_chars(self): instance = self.ddf.get(ModelWithStrings, string=Mask('___')) assert re.match(r'[a-z]{3}', instance.string) def test_it_must_generate_upper_ascii_chars(self): instance = self.ddf.get(ModelWithStrings, string=Mask('---')) assert re.match(r'[A-Z]{3}', instance.string) def test_it_must_accept_pure_chars(self): instance = self.ddf.get(ModelWithStrings, string=Mask('ABC123')) assert re.match(r'ABC123', instance.string) def test_it_must_be_able_to_escape_symbols(self): instance = self.ddf.get(ModelWithStrings, string=Mask(r'!# !_ !-')) assert '# _ -' == instance.string def test_phone_mask(self): instance = self.ddf.get(ModelWithStrings, string=Mask(r'+## (##) #####!-#####')) assert re.match(r'\+\d{2} \(\d{2}\) \d{5}-\d{5}', instance.string) def test_address_mask(self): instance = self.ddf.get(ModelWithStrings, string=Mask(r'St. -______, ### !- -- --')) assert re.match(r'St\. [A-Z]{1}[a-z]{6}, \d{3} - [A-Z]{2} [A-Z]{2}', instance.string) django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_module_ddf_shortcut.py000066400000000000000000000004641450115615700314210ustar00rootroot00000000000000 try: from ddf import N, G, F, C, P, PRE_SAVE, POST_SAVE from ddf import new, get, fixture, teach, look_up_alias from ddf import skip_for_database, only_for_database from ddf import FileSystemDjangoTestCase except ImportError: assert False, 'Import `ddf` module is not working properly.' django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_sample_app.py000066400000000000000000000036461450115615700275120ustar00rootroot00000000000000import pytest from django.test import TestCase from ddf import * class SampleAppTestCase(TestCase): def test_publisher(self): o = G('django_dynamic_fixture.Publisher') assert o.name is not None def test_author(self): o = G('django_dynamic_fixture.Author') assert o.name is not None assert o.description is None o = G('django_dynamic_fixture.Author', fill_nullable_fields=True) assert o.description is not None def test_category(self): o = G('django_dynamic_fixture.Category') assert o.name is not None assert o.parent is None o = G('django_dynamic_fixture.Category', fk_min_depth=1) assert o.parent is not None assert o.parent.parent is None o = G('django_dynamic_fixture.Category', fk_min_depth=2) assert o.parent.parent is not None assert o.parent.parent.parent is None def test_book(self): o = G('django_dynamic_fixture.Book') assert o.main_author is not None assert o.main_author.name is not None assert o.authors.all().count() == 0 assert o.categories.all().count() == 0 o = G('django_dynamic_fixture.Book', authors=2, categories=5) assert o.authors.all().count() == 2 assert o.categories.all().count() == 5 o = G('django_dynamic_fixture.Book', metadata={'a': 1}) assert o.metadata == {'a': 1} def test_book_edition(self): o = G('django_dynamic_fixture.BookEdition') assert o.book is not None assert o.year is not None assert o.publishers.all().count() == 0 o = G('django_dynamic_fixture.BookEdition', publishers=2) assert o.publishers.all().count() == 2 def test_book_publisher(self): o = G('django_dynamic_fixture.BookPublisher') assert o.book_edition.book is not None assert o.publisher is not None assert o.comments is not None django-dynamic-fixture-4.0.1/django_dynamic_fixture/tests/test_wrappers.py000066400000000000000000000274501450115615700272330ustar00rootroot00000000000000 from django.test import TransactionTestCase as TestCase from django_dynamic_fixture.models_test import EmptyModel, ModelWithRelationships, ModelForLibrary, ModelWithStrings from django_dynamic_fixture import N, G, F, C, M, P, teach, look_up_alias, PRE_SAVE, POST_SAVE class NShortcutTest(TestCase): def test_shortcut_N(self): instance = N(EmptyModel) assert instance.id == None class GShortcutTest(TestCase): def test_shortcut_G(self): instance = G(EmptyModel) assert instance.id != None class PShortcutTest(TestCase): def test_accept_model_instance(self): P(N(EmptyModel)) P(G(EmptyModel)) def test_accepts_list(self): P([N(EmptyModel), G(EmptyModel)]) def test_accepts_tuple(self): P((N(EmptyModel), G(EmptyModel))) def test_accepts_queryset(self): P(EmptyModel.objects.all()) class FShortcutTest(TestCase): def test_fk(self): instance = G(ModelWithRelationships, integer=1000, foreignkey=F(integer=1001)) assert 1000, instance.integer == 1000 assert instance.foreignkey.integer == 1001 def test_self_fk(self): instance = G(ModelWithRelationships, integer=1000, selfforeignkey=F(integer=1001)) assert instance.integer == 1000 assert instance.selfforeignkey.integer == 1001 def test_o2o(self): instance = G(ModelWithRelationships, integer=1000, onetoone=F(integer=1001)) assert instance.integer == 1000 assert instance.onetoone.integer == 1001 def test_m2m_with_one_element(self): instance = G(ModelWithRelationships, integer=1000, manytomany=[F(integer=1001)]) assert instance.integer == 1000 assert instance.manytomany.all()[0].integer == 1001 def test_m2m_with_many_elements(self): instance = G(ModelWithRelationships, integer=1000, manytomany=[F(integer=1001), F(integer=1002)]) assert instance.integer == 1000 assert instance.manytomany.all()[0].integer == 1001 assert instance.manytomany.all()[1].integer == 1002 def test_full_example(self): instance = G(ModelWithRelationships, integer=1000, foreignkey=F(integer=1001), selfforeignkey=F(integer=1002), onetoone=F(integer=1003), manytomany=[F(integer=1004), F(integer=1005), F(selfforeignkey=F(integer=1006))]) assert instance.integer == 1000 assert instance.foreignkey.integer == 1001 assert instance.selfforeignkey.integer == 1002 assert instance.onetoone.integer == 1003 assert instance.manytomany.all()[0].integer == 1004 assert instance.manytomany.all()[1].integer == 1005 assert instance.manytomany.all()[2].selfforeignkey.integer == 1006 def test_two_properties_same_object(self): instance = G(ModelWithRelationships, integer=1000, foreignkey=F(integer=1001, integer_b=1002)) assert instance.integer == 1000 assert instance.foreignkey.integer == 1001 assert instance.foreignkey.integer_b == 1002 def test_using_look_up_alias(self): instance = G(ModelWithRelationships, integer=1000, foreignkey__integer=1001, selfforeignkey__integer=1002, onetoone__integer=1003, manytomany=[F(integer=1004), F(integer=1005), F(selfforeignkey__integer=1006)]) assert instance.integer == 1000 assert instance.foreignkey.integer == 1001 assert instance.selfforeignkey.integer == 1002 assert instance.onetoone.integer == 1003 assert instance.manytomany.all()[0].integer == 1004 assert instance.manytomany.all()[1].integer == 1005 assert instance.manytomany.all()[2].selfforeignkey.integer == 1006 def test_using_look_up_alias_two_properties_same_object(self): instance = G(ModelWithRelationships, integer=1000, foreignkey__integer=1001, foreignkey__integer_b=1002) assert instance.integer == 1000 assert instance.foreignkey.integer == 1001 assert instance.foreignkey.integer_b == 1002 def test_using_look_up_alias_two_properties_same_object(self): instance = G(ModelWithRelationships, integer=1000, foreignkey__integer=1001, foreignkey__integer_b=1002) assert instance.integer == 1000 assert instance.foreignkey.integer_b == 1002 assert instance.foreignkey.integer == 1001 class CShortcutTest(TestCase): def test_copying_from_the_same_model(self): instance = G(ModelWithRelationships, integer=C('integer_b')) assert instance.integer == instance.integer_b def test_copying_from_a_fk(self): instance = G(ModelWithRelationships, foreignkey=F(), integer=C('foreignkey.integer')) assert instance.integer == instance.foreignkey.integer def test_copying_from_a_one2one(self): instance = G(ModelWithRelationships, onetoone=F(), integer=C('onetoone.integer')) assert instance.integer == instance.onetoone.integer def test_copying_from_a_self_fk(self): instance = G(ModelWithRelationships, selfforeignkey=F(), integer=C('selfforeignkey.integer_b')) assert instance.integer == instance.selfforeignkey.integer_b def test_copying_inside_fk(self): instance = G(ModelWithRelationships, selfforeignkey=F(selfforeignkey=F(), integer=C('selfforeignkey.integer_b'))) assert instance.selfforeignkey.integer == instance.selfforeignkey.selfforeignkey.integer_b def test_copying_inside_many_to_many(self): instance = G(ModelWithRelationships, manytomany=[F(integer=C('integer_b'))]) instance1 = instance.manytomany.all()[0] assert instance1.integer == instance1.integer_b class MShortcutTest(TestCase): def test_full_data_mask_sample(self): import re instance = G(ModelWithStrings, string=M(r'St. -______, ### !- -- --')) assert re.match(r'St\. [A-Z]{1}[a-z]{6}, \d{3} - [A-Z]{2} [A-Z]{2}', instance.string) class TeachingAndLessonsTest(TestCase): def test_global_lesson(self): teach(ModelForLibrary, integer=1000) instance = G(ModelForLibrary) assert instance.integer == 1000 instance = G(ModelForLibrary, integer=1001) assert instance.integer == 1001 instance = G(ModelForLibrary) assert instance.integer == 1000 class CreatingMultipleObjectsTest(TestCase): def test_new(self): assert N(EmptyModel, n=0) == [] assert N(EmptyModel, n= -1) == [] assert isinstance(N(EmptyModel), EmptyModel) # default is 1 assert isinstance(N(EmptyModel, n=1), EmptyModel) assert len(N(EmptyModel, n=2)) == 2 def test_get(self): assert G(EmptyModel, n=0) == [] assert G(EmptyModel, n= -1) == [] assert isinstance(G(EmptyModel), EmptyModel) # default is 1 assert isinstance(G(EmptyModel, n=1), EmptyModel) assert len(G(EmptyModel, n=2)) == 2 class LookUpSeparatorTest(TestCase): def test_look_up_alias_with_all_params_combination(self): assert {'a': 1} == look_up_alias(a=1, ddf_as_f=False) assert {'a': 1, 'b': 2} == look_up_alias(a=1, b=2, ddf_as_f=False) assert {'a': {'b': 1}} == look_up_alias(a__b=1, ddf_as_f=False) assert {'a': {'b': 1}, 'c': 2} == look_up_alias(a__b=1, c=2, ddf_as_f=False) assert {'a': {'b': 1}, 'c': {'d': 2}} == look_up_alias(a__b=1, c__d=2, ddf_as_f=False) assert {'a': {'b': 1, 'c': 2}} == look_up_alias(a__b=1, a__c=2, ddf_as_f=False) assert {'a': {'b': 1, 'c': {'d': 2}}} == look_up_alias(a__b=1, a__c__d=2, ddf_as_f=False) assert {'a': {'b': 1, 'c': {'d': 2}}, 'e': {'f': 3}, 'g': 4} == look_up_alias(a__b=1, a__c__d=2, e__f=3, g=4, ddf_as_f=False) def test_look_up_alias_as_f_with_all_params_combination(self): assert {'a': 1} == look_up_alias(a=1) assert {'a': 1, 'b': 2} == look_up_alias(a=1, b=2) assert {'a': F(b=1)} == look_up_alias(a__b=1) assert {'a': F(b=1), 'c': 2} == look_up_alias(a__b=1, c=2) assert {'a': F(b=1), 'c': F(d=2)} == look_up_alias(a__b=1, c__d=2) assert {'a': F(b=1, c=2)} == look_up_alias(a__b=1, a__c=2) assert {'a': F(b=1, c=F(d=2))} == look_up_alias(a__b=1, a__c__d=2) assert {'a': F(b=1, c=F(d=2)), 'e': F(f=3), 'g': 4} == look_up_alias(a__b=1, a__c__d=2, e__f=3, g=4) def test_look_up_alias_with_just_one_parameter(self): assert {'a': 1} == look_up_alias(a=1) assert {'a': F()} == look_up_alias(a=F()) assert {'a_b': 1} == look_up_alias(a_b=1) assert {'a': F(b=1)} == look_up_alias(a__b=1) assert {'a_b': F(c=1)} == look_up_alias(a_b__c=1) assert {'a': F(b=F(c=1))} == look_up_alias(a__b__c=1) assert {'a_b': F(c_d=F(e_f=1))} == look_up_alias(a_b__c_d__e_f=1) def test_look_up_alias_with_many_parameters(self): assert {'a': 1, 'b': 2} == look_up_alias(a=1, b=2) assert {'a': 1, 'b_c': 2} == look_up_alias(a=1, b_c=2) assert {'a': 1, 'b': F(c=2)} == look_up_alias(a=1, b__c=2) assert {'a': F(b=1), 'c': F(d=2)} == look_up_alias(a__b=1, c__d=2) def test_dont_format_default_dict_values(self): kwargs = dict(metadata=dict(a=1)) assert {'metadata': {'a': 1}} == look_up_alias(**kwargs) assert {'metadata': {'a': 1}} == look_up_alias(metadata={'a': 1}) class PreAndPostSaveTest(TestCase): def tearDown(self): # Workaround to pass the tests in Travis, caused by an unknown issue # with LazySettings and ALLOWED_HOSTS pass def test_pre_save(self): PRE_SAVE(EmptyModel, lambda x: x) def test_post_save(self): POST_SAVE(EmptyModel, lambda x: x) class UsingModelNameInsteadTest(TestCase): def test_compatibility_for_new(self): instance = N('django_dynamic_fixture.ModelWithNumbers', integer=5) assert instance.integer == 5 def test_compatibility_for_get(self): instance = G('django_dynamic_fixture.ModelWithNumbers', integer=5) assert instance.integer == 5 def test_compatibility_for_teach(self): teach('django_dynamic_fixture.ModelWithDefaultValues', integer_with_default=5) instance = G('django_dynamic_fixture.ModelWithDefaultValues') assert instance.integer_with_default == 5 class OverrideDataFixture(TestCase): def test_random(self): instance = N('django_dynamic_fixture.ModelWithNumbers', data_fixture='random') assert instance is not None instance = G('django_dynamic_fixture.ModelWithNumbers', data_fixture='random') assert instance is not None def test_sequential(self): instance = N('django_dynamic_fixture.ModelWithNumbers', data_fixture='sequential') assert instance is not None instance = G('django_dynamic_fixture.ModelWithNumbers', data_fixture='sequential') assert instance is not None def test_static_sequential(self): instance = N('django_dynamic_fixture.ModelWithNumbers', data_fixture='static_sequential') assert instance is not None instance = G('django_dynamic_fixture.ModelWithNumbers', data_fixture='static_sequential') assert instance is not None def test_custom_one(self): from django_dynamic_fixture.fixture_algorithms.sequential_fixture import SequentialDataFixture instance = N('django_dynamic_fixture.ModelWithNumbers', data_fixture=SequentialDataFixture()) assert instance is not None instance = G('django_dynamic_fixture.ModelWithNumbers', data_fixture=SequentialDataFixture()) assert instance is not None class Version(TestCase): def test_version(self): from django_dynamic_fixture import __version__ from ddf import __version__ django-dynamic-fixture-4.0.1/docs/000077500000000000000000000000001450115615700170215ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/docs/Makefile000066400000000000000000000151621450115615700204660ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = ../env/bin/sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DDF.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DDF.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/DDF" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/DDF" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." django-dynamic-fixture-4.0.1/docs/source/000077500000000000000000000000001450115615700203215ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/docs/source/_static/000077500000000000000000000000001450115615700217475ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/docs/source/_static/coverage.html000066400000000000000000000432371450115615700244410ustar00rootroot00000000000000 Coverage report

Coverage report: 92%

coverage.py v7.3.0, created at 2023-09-15 17:34 -0500

Module statements missing excluded coverage
django_dynamic_fixture/__init__.py 91 2 0 98%
django_dynamic_fixture/ddf.py 405 36 9 91%
django_dynamic_fixture/decorators.py 21 0 0 100%
django_dynamic_fixture/django_helper.py 133 16 0 88%
django_dynamic_fixture/fdf.py 128 13 2 90%
django_dynamic_fixture/fields.py 34 13 5 62%
django_dynamic_fixture/fixture_algorithms/__init__.py 12 0 0 100%
django_dynamic_fixture/fixture_algorithms/default_fixture.py 59 22 0 63%
django_dynamic_fixture/fixture_algorithms/random_fixture.py 77 6 0 92%
django_dynamic_fixture/fixture_algorithms/sequential_fixture.py 105 7 6 93%
django_dynamic_fixture/fixture_algorithms/tests/__init__.py 0 0 0 100%
django_dynamic_fixture/fixture_algorithms/tests/abstract_test_generic_fixture.py 47 2 0 96%
django_dynamic_fixture/fixture_algorithms/tests/test_default_fixture.py 39 23 0 41%
django_dynamic_fixture/fixture_algorithms/tests/test_default_fixture_postgres.py 49 2 0 96%
django_dynamic_fixture/fixture_algorithms/tests/test_random_fixture.py 6 0 0 100%
django_dynamic_fixture/fixture_algorithms/tests/test_sequential_fixture.py 32 0 0 100%
django_dynamic_fixture/fixture_algorithms/tests/test_unique_random_fixture.py 39 0 0 100%
django_dynamic_fixture/fixture_algorithms/unique_random_fixture.py 116 13 3 89%
django_dynamic_fixture/global_settings.py 49 3 0 94%
django_dynamic_fixture/models.py 4 0 0 100%
django_dynamic_fixture/models_sample_app.py 25 0 0 100%
django_dynamic_fixture/models_test.py 291 13 0 96%
django_dynamic_fixture/models_third_party.py 43 14 0 67%
django_dynamic_fixture/script_ddf_checkings.py 60 20 0 67%
django_dynamic_fixture/tests/__init__.py 0 0 0 100%
django_dynamic_fixture/tests/conftest.py 13 2 0 85%
django_dynamic_fixture/tests/test_ddf.py 390 7 0 98%
django_dynamic_fixture/tests/test_ddf_checkings.py 22 0 0 100%
django_dynamic_fixture/tests/test_ddf_copier.py 52 0 0 100%
django_dynamic_fixture/tests/test_ddf_custom_fields.py 95 25 0 74%
django_dynamic_fixture/tests/test_ddf_custom_models.py 19 2 0 89%
django_dynamic_fixture/tests/test_ddf_geo.py 31 12 0 61%
django_dynamic_fixture/tests/test_ddf_signals.py 76 4 0 95%
django_dynamic_fixture/tests/test_ddf_teaching_and_lessons.py 158 0 0 100%
django_dynamic_fixture/tests/test_decorators.py 33 0 0 100%
django_dynamic_fixture/tests/test_django_helper.py 159 0 0 100%
django_dynamic_fixture/tests/test_fdf.py 134 0 0 100%
django_dynamic_fixture/tests/test_global_settings.py 86 0 0 100%
django_dynamic_fixture/tests/test_mask.py 31 0 0 100%
django_dynamic_fixture/tests/test_module_ddf_shortcut.py 7 2 0 71%
django_dynamic_fixture/tests/test_sample_app.py 46 0 0 100%
django_dynamic_fixture/tests/test_wrappers.py 203 4 0 98%
Total 3420 263 25 92%

No items found using the specified filter.

django-dynamic-fixture-4.0.1/docs/source/about.rst000066400000000000000000000060241450115615700221670ustar00rootroot00000000000000.. about: About ******************************************************************************* .. contents:: :local: Collaborators =============================================================================== * Paulo Cheque - Master Dissertation about Automated Test Patterns: https://teses.usp.br/teses/disponiveis/45/45134/tde-02042012-120707/pt-br.php * Valder Gallo * Julio Netto Pull Requests tips =============================================================================== About commit messages ------------------------------------------------------------------------------- * Messages in english only * All messages have to follow the pattern: "[TAG] message" * TAG have to be one of the following: new, update, bugfix, delete, refactoring, config, log, doc, mergefix About the code ------------------------------------------------------------------------------- * One change (new feature, update, refactoring, bugfix etc) by commit * All bugfix must have a test simulating the bug * All commit must have 100% of test coverage Running tests ------------------------------------------------------------------------------- Command:: python manage.py test --with-coverage --cover-inclusive --cover-html --cover-package=django_dynamic_fixture.* --with-queries --with-ddf-setup TODO list =============================================================================== Tests and Bugfixes ------------------------------------------------------------------------------- * with_queries bugfixes (always print 0 queries) * Deal with relationships with dynamic related_name * bugfix in fdf or ddf: some files/directories are not deleted * tests with files in ddf * tests with proxy models * tests with GenericRelations, GenericForeignKey etc * more tests with OneToOneField(parent_link=True) Features ------------------------------------------------------------------------------- * auto config of denormalizated fields * related_name documentation or workaround * today, yesterday, tomorrow on fdf * string generation according to a regular expression Documentation ------------------------------------------------------------------------------- * with_queries documentation * example to generate models with validators in fields or in clean methods External references =============================================================================== * http://stackoverflow.com/search?q=django+dynamic+fixture * http://stackoverflow.com/questions/12487337/optimizing-setup-and-teardown-for-sample-django-model-using-django-nose-and-djan * http://stackoverflow.com/questions/4400609/initial-data-fixture-management-in-django Running tests locally =============================================================================== Install GDAL: https://docs.djangoproject.com/en/1.11/ref/contrib/gis/install/geolibs/#gdal Commands:: make build make tox django-dynamic-fixture-4.0.1/docs/source/change_log.rst000066400000000000000000000350041450115615700231430ustar00rootroot00000000000000.. about: Change Log ******************************************************************************* .. contents:: :local: Date format: yyyy/mm/dd Version 4.0.1 - 2023/09/15 ------------------------------------------------------------------------------- * * Reuse lessons in Proxy models * https://github.com/paulocheque/django-dynamic-fixture/pull/161 Version 4.0.0 - 2023/08/26 ------------------------------------------------------------------------------- * * Removed compatibilty of Python 2 * Removed compatibilty of Django 3 or lower * Removed Nose plugins * Removed Coveralls integration Version 3.1.3 - 2023/08/18 ------------------------------------------------------------------------------- * * Removed deprecation warnings: PR #150 * Fixed doc typos: PR #151 * Allow Mask objects as lessons for unique fields: PR #153 Version 3.1.2 - 2021/10/01 ------------------------------------------------------------------------------- * * Correct type for TimeField: PR #139 * Catch Django 3.2 TypeError instantiating abstracts: PR #142 * ci: update tox.ini and travis.yml to test Django3.1/3.2: PR #143 Version 3.1.1 - 2020/11/17 ------------------------------------------------------------------------------- * * Fixed importing FieldDoesNotExist exception class for Django==3.1: PR #134 Version 3.1.0 - 2020/03/29 ------------------------------------------------------------------------------- * * Compatibility check report improvements: PR #131 * DDF now accepts to override a DDF lesson: PR #130 * New feature: DDF Mask: ``M``: PR #129 * Option to configure DDF via Environment variables Version 3.0.3 - 2020/03/01 ------------------------------------------------------------------------------- * * Setting ``DDF_NUMBER_OF_LAPS`` was removed in favor of the new ``DDF_FK_MIN_DEPTH`` * Parameter ``number_of_laps`` was removed in favor of new ``fk_min_depth`` * Important bugfix for the FK field management: Issue #120 * Give priority to default values instead of null values: Issue #121 * Fixed lookup fields that was breaking JSONFields: Issue #122 Version 3.0.2 - 2020/01/10 ------------------------------------------------------------------------------- * * Changed ``VERSION`` to ``__version__``: ``from ddf import __version`` * Changed ``lesson`` to ``ddf_lesson``: ``G(Model, ddf_lesson='...')`` Version 3.0.1 - 2020/01/07 ------------------------------------------------------------------------------- * * Fixed importing ``from ddf import ddf_check_models`` * Added a CSV report for the ddf_check_models * Added the VERSION property in ddf: ``from ddf import VERSION`` Version 3.0.0 - 2020/01/05 ------------------------------------------------------------------------------- * * New ``teach`` method that replaced the old ``shelve=True``. * Support to ``app_label.ModelName`` strings instead of the model class. * New ``ddf`` shortcut: ``from ddf import G, N, C, P, teach``. * Support for **Django 3.0.0**. * New ``ddf_check_models`` method to print a compatibility report. * Added Python 3 Type Hints. * Added native support for the ``Postgres.JSONField``. * Added native support for Django-Polymorphic models. * Bugfix for the ``data_fixture`` parameter to accept string alias. * Another small bugfixes with ``choices`` parameter. * Many internal refactorings. * Travis/Tests integration fixed. * Documentation organised, updated and simplified. * Changed the default value of the ``persist_dependencies`` in ``N`` from True to False. * DDF_USE_LIBRARY settings removed. Now, it is always activated by default. * DDF_VALIDATE_ARGS settings removed. Now, it is always activated by default. * DDF_FILL_NULLABLE_FIELDS default changed from True to False * DDF_NUMBER_OF_LAPS default changed from 1 to 0 * Bugfix for named primary keys. Version 2.0.0 - 2017/12/08 ------------------------------------------------------------------------------- * * `DDF_IGNORE_FIELDS` globally for all new instances * Bugfix for auto generated `_ptr` fields. * Support for Django 2.0.0. Version 1.9.5 - 2017/05/09 ------------------------------------------------------------------------------- * * Bugfix: avoid GDALException on Django 1.11 Version 1.9.4 - 2017/04/17 ------------------------------------------------------------------------------- * * Added support for django.contrib.postgres.fields.ArrayField field * Fixed GeoDjango Point instantiation. Version 1.9.3 - 2017/03/08 ------------------------------------------------------------------------------- * * Improve compatibility for the DDF internal tests Version 1.9.2 - 2017/03/08 ------------------------------------------------------------------------------- * * Django 2.0 compatibility * New: Support for wildcards `?` and `*` in the ignore fields * Bugfix: Fixed DDF_TEST_GEODJANGO test issues Version 1.9.1 - 2016/12/21 ------------------------------------------------------------------------------- * * Bugfix: Django version parser * Bugfix: NameError on invalid variable name Version 1.9.0 - 2016/05/23 ------------------------------------------------------------------------------- * * [New] Django 1.9 support * [Bugfix] Fixed issue on ForeignKey field with default id * [Bugfix] Fixed issue with SimpleUploadedFile Version 1.8.4 - 2015/05/26 ------------------------------------------------------------------------------- * * [New] UUIDField support * [New] GeoDjango fields support (GeometryField, PointField, LineStringField, PolygonField, MultiPointField, MultiLineStringField, MultiPolygonField, GeometryCollectionField) * [Update] Better error messages * [Bugfix] BinaryField fixture fix * [Update] Optimizations Version 1.8.3 - 2015/05 ------------------------------------------------------------------------------- * * [Update] No more deprecated methods Version 1.8.2 - 2015/05 ------------------------------------------------------------------------------- * * [New] Support for Django 1.8 Version 1.8.1 - 2014/12 ------------------------------------------------------------------------------- * * [Update] Avoid conflicts with "instance" and "field" model field names. Version 1.8.0 - 2014/09 ------------------------------------------------------------------------------- * * [New] DDF_FIELD_FIXTURES global settings. You can include support of other fields here. * [New] Support for BinaryField * [Update] Django 1.7, Python 3.4 and Pypy official suppport (fixed some tests) * [New] ReadTheDocs full documentation * [Update] Fixed some print calls for python 3 * [Update] Nose plugin disable as default. Recommended behavior of nose plugins. * [Update] ignore_fields parameter does not consider fields explicitly defined by the developer. * [Update] Travis env using Tox Version 1.7.0 - 2014/03/26 ------------------------------------------------------------------------------- * Version 1.6.4 - 2012/12/30 ------------------------------------------------------------------------------- * * [Bugfix] auto_now and auto_now_add must not be disabled forever (thanks for reporting) * [New] Added global_sequential data fixture (Pull request, thanks) Version 1.6.3 - 2012/04/10 ------------------------------------------------------------------------------- * * [New] Pre save and post save special signals Version 1.6.2 - 2012/04/09 ------------------------------------------------------------------------------- * * [New] Debug Mode and option (global/local) to enable/disable debug mode Version 1.6.1 - 2012/04/07 ------------------------------------------------------------------------------- * * [New] New alias for F: field1__field2=value instead of field1=F(field2=value) * [New] Named shelves inherit from default shelve Version 1.6.0 - 2012/03/31 ------------------------------------------------------------------------------- * * [New] Copier: option to copy a generated value for a field to another one. Useful for denormalizated fields. * [New] Shelve/Library: option to store a default configuration of a specific model. Useful to avoid replicated code of fixtures. Global option: DDF_USE_LIBRARY. * [New] Named Shelve: option to store multiple configurations for a model in the library. * [New] Nose plugin for global set up. * [New] P function now accept a queryset. Version 1.5.1 - 2012/03/26 ------------------------------------------------------------------------------- * * [New] global option: DDF_VALIDATE_ARGS that enable or disable field names. * [Bugfix] F feature stop working. Version 1.5.0 - 2012/03/25 ------------------------------------------------------------------------------- * * [New] global settings: DDF_DEFAULT_DATA_FIXTURE, DDF_FILL_NULLABLE_FIELDS, DDF_IGNORE_FIELDS, DDF_NUMBER_OF_LAPS, DDF_VALIDATE_MODELS * [New] new data fixture that generates random data * [New] new data fixture that use sequential numbers only for fields that have unique=True * [New] P function now accept a list of model instances * [New] Option to call model_instance.full_clean() validation method before saving the object (DDF_VALIDATE_MODELS). * [New] Validate field names. If a invalid field name is passed as argument, it will raise an InvalidConfigurationError exception. * [Bugfix] DateField options 'auto_add_now' and 'auto_add' are disabled if a custom value is used. Version 1.4.3 - 2012/02/23 ------------------------------------------------------------------------------- * * [Bugfix] Bugfix in ForeignKeys with default values Version 1.4.2 - 2011/11/07 ------------------------------------------------------------------------------- * * [Bugfix] Bugfix in FileSystemDjangoTestCase Version 1.4.1 - 2011/11/07 ------------------------------------------------------------------------------- * * [New] Now you can set a custom File to a FileField and the file will be saved in the file storage system. * **FileSystemDjangoTestCase**: * [New] create_django_file_using_file create a django.File using the content of your file * [New] create_django_file_with_temp_file now accepts a content attribute that will be saved in the generated file * [Bugfix] now create_django_file_with_temp_file close the generated file Version 1.4.0 - 2011/10/29 ------------------------------------------------------------------------------- * * [New] Nose plugin to count queries on each test * [New] Command line to count queries on the save (insert and update) of each model * [Update] Field with choice and default must use the default value, not the first choice value * [Update] Validation if the class is a models.Model instance * [Update] Showing all stack trace, when an exception occurs * **Decorators**: * [Bugfix] default values of database engines were not used correctly * **FileSystemDjangoTestCase**: * [Testfix] Fixing tests Version 1.3.1 - 2011/10/03 ------------------------------------------------------------------------------- * * [Bugfix] Bugfixes in FileSystemDjangoTestCase Version 1.3.0 - 2011/10/03 ------------------------------------------------------------------------------- * * [New] File System Django Test Case * [New] Decorators skip_for_database and only_for_database * [Bugfix] Inheritance problems, before this version the DDF filled fields with the attribute parent_link Version 1.2.3 - 2011/06/27 ------------------------------------------------------------------------------- * * [Bugfix] string truncation to max_length Version 1.2.2 - 2011/05/05 ------------------------------------------------------------------------------- * * [Update] Improvements in exception messages Version 1.2.1 - 2011/03/11 ------------------------------------------------------------------------------- * * [Bugfix] Propagate ignored fields to self references * [Refact] Refactoring Version 1.2 - 2011/03/04 ------------------------------------------------------------------------------- * * [New] ignore_fields * [New] now it is possible to set the ID Version 1.1 ------------------------------------------------------------------------------- * (1.0 has the 1.1 package) * [Bugfix] Bug fixes for 1.0 Version 1.0 ------------------------------------------------------------------------------- * Initial version * Ready to use in big projects django-dynamic-fixture-4.0.1/docs/source/conf.py000066400000000000000000000200131450115615700216140ustar00rootroot00000000000000# # DDF documentation build configuration file, created by # sphinx-quickstart on Mon Sep 1 18:20:45 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.todo', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'DDF' copyright = '2014, Paulo Cheque' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '4.0.1' # The full version, including alpha/beta/rc tags. release = '4.0.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'DDFdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'DDF.tex', 'DDF Documentation', 'Paulo Cheque', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'ddf', 'DDF Documentation', ['Paulo Cheque'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'DDF', 'DDF Documentation', 'Paulo Cheque', 'DDF', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False django-dynamic-fixture-4.0.1/docs/source/data.rst000066400000000000000000000131541450115615700217700ustar00rootroot00000000000000.. _data: Field Data Generation ******************************************************************************* .. contents:: :local: Supported Fields =============================================================================== * **Numbers**: IntegerField, SmallIntegerField, PositiveIntegerField, PositiveSmallIntegerField, BigIntegerField, FloatField,DecimalField * **Strings**: CharField, TextField, SlugField, CommaSeparatedintegerField * **Booleans**: BooleanField, NullBooleanField * **Timestamps**: DateField, TimeField, DatetimeField * **Utilities**: EmailField, UrlField, IPAddressField, GenericIPAddressField, XMLField, UUIDField * **Files**: FilePathField, FileField, ImageField * **Binary**: BinaryField * **Postgres**: JSONField, ArrayField * **GIS/Django Geo**: Geometryfield, PointField, LineStringField, PolygonField, MultiPointField, MultiLineStringField, MultiPolygonField, GeometryCollectionField Use ``DDF_FIELD_FIXTURES`` settings, customized data or even the field default values to deal with not supported fields. GeoDjango Fields =============================================================================== After `1.8.4` version, DDF has native support for GeoDjango fields: GeometryField, PointField, LineStringField, PolygonField, MultiPointField, MultiLineStringField, MultiPolygonField, GeometryCollectionField. For older versions of DDF, please, use the following approach: You can use ``DDF_FIELD_FIXTURES`` to create fixtures for Geo Django fields:: # https://docs.djangoproject.com/en/dev/ref/contrib/gis/ from django.contrib.gis.geos import Point DDF_FIELD_FIXTURES = { 'django.contrib.gis.db.models.GeometryField': lambda: None, 'django.contrib.gis.db.models.PointField': lambda: None, 'django.contrib.gis.db.models.LineStringField': lambda: None, 'django.contrib.gis.db.models.PolygonField': lambda: None, 'django.contrib.gis.db.models.MultiPointField': lambda: None, 'django.contrib.gis.db.models.MultiLineStringField': lambda: None, 'django.contrib.gis.db.models.MultiPolygonField': lambda: None, 'django.contrib.gis.db.models.GeometryCollectionField': lambda: None, } About Custom Fields =============================================================================== * Customized data is also valid for unsupported fields. * You can also set the field fixture using the ``DDF_FIELD_FIXTURES`` settings. (new in 1.8.0) * if a field is not default in Django, but it inherits from a Django field, it will be filled using its config. * if a field is not default in Django and not related with a Django field, it will raise an ``UnsupportedFieldError``. * if it does not recognize the Field class, it will raise an ``UnsupportedFieldError``. Fill Nullable Fields =============================================================================== This option define if nullable fields (fields with ``null=True``) will receive a generated data or not (``data=None``). This property is exclusive for non-FK fields. It is possible to override the global option for an specific instance. But be careful because this option will be propagate to all internal dependencies. In settings.py:: DDF_FILL_NULLABLE_FIELDS = True In the test file:: instance = G(MyModel, fill_nullable_fields=False) assert instance.a_nullable_field is None assert instance.a_required_field is not None instance = G(MyModel, fill_nullable_fields=True) assert instance.a_nullable_field is not None assert instance.a_required_field is not None Ignoring Fields (New in 1.2.0) =============================================================================== This option defines a list of fields DDF will ignore. In other words, DDF will not fill it with dynamic data. This option can be useful for fields auto calculated by models, like [MPTT](https://github.com/django-mptt/django-mptt) models. Ignored fields are propagated ONLY to self references. In settings.py:: DDF_IGNORE_FIELDS = ['field_x', 'field_y'] # default = [] Ignored field names can use wildcard matching with '*' and '?' characters which substitute multiple or one character respectively. Wildcards are useful for fields that should not be populated and which match a pattern, like ``*_ptr`` fields for [django-polymorphic](https://github.com/django-polymorphic/django-polymorphic). In settings.py:: DDF_IGNORE_FIELDS = ['*_ptr'] # Ignore django-polymorphic pointer fields It is not possible to override the global configuration, just extend the list. So use global option with caution:: instance = G(MyModel, ignore_fields=['another_field_name']) assert instance.another_field_name is None Minimum Foreign Key Depth =============================================================================== This option is used by DDF to control dependencies and cyclic dependencies (``ForeignKey`` by ``self``, denormalizations, bad design etc). DDF does not enter infinite loop of instances generation. This option defines how depth DDF should go to create instances of foreign key fields. This option can also be used to create trees with different lengths. In settings.py:: DDF_FK_MIN_DEPTH = 0 In the test file:: instance = G(MyModel, fk_min_depth=1) assert instance.self_fk.id is not None assert instance.self_fk.self_fk.id is None instance = G(MyModel, fk_min_depth=2) assert instance.self_fk.id is not None assert instance.self_fk.self_fk.id is not None assert instance.self_fk.self_fk.self_fk.id is None > Incompatibility warning: Before DDF 3.0.3, DDF handled FK cycles instead of FK depth, through the removed properties `DDF_NUMBER_OF_LAPS` and `number_of_laps`. django-dynamic-fixture-4.0.1/docs/source/data_fixtures.rst000066400000000000000000000121761450115615700237240ustar00rootroot00000000000000.. _data_fixtures: Data Fixtures ******************************************************************************* .. contents:: :local: Configuration =============================================================================== This configuration defines the algorithm of data generation that will be used to populate fields with dynamic data. Do NOT mix data fixtures in the same test suite because the generated data may conflict and it will produce erratic tests. In settings.py:: DDF_DEFAULT_DATA_FIXTURE = 'sequential' # or 'static_sequential' or 'random' or 'path.to.your.DataFixtureClass' Overriding global data fixture =============================================================================== This algorithm will be used just for the current model generation. In the test file or shell:: G(MyModel, data_fixture='sequential') G(MyModel, data_fixture='random') G(MyModel, data_fixture=MyCustomDataFixture()) Sequential Data Fixture =============================================================================== Useful to use in test suites. Sequential Data Fixture stores an independent counter for each model field that is incremented every time this field has to be populated. If for some reason the field has some restriction (*max_length*, *max_digits* etc), the counter restarts. This counter is used to populate fields of numbers (*Integer*, *BigDecimal* etc) and strings (*CharField*, *TextField* etc). For *BooleanFields*, it will always return False. For *NullBooleanFields*, it will always return None. For date and time fields, it will always return Today minus 'counter' days or Now minus 'counter' seconds, respectively. In settings.py:: DDF_DEFAULT_DATA_FIXTURE = 'sequential' In the test file:: instance = G(MyModel) assert instance.integerfield_a == 1 assert instance.integerfield_b == 1 assert instance.charfield_b == 1 assert instance.booleanfield == False assert instance.nullbooleanfield is None instance = G(MyModel) assert instance.integerfield_a == 2 assert instance.integerfield_b == 2 assert instance.charfield_b == 2 assert instance.booleanfield == False assert instance.nullbooleanfield is None instance = G(MyOtherModel) assert instance.integerfield_a == 1 # ... Static Sequential Data Fixture =============================================================================== Useful to use in test suites. Static Sequential Data Fixture is the same as Sequential Data Fixture, except it will increase the counter only if the field has *unique=True*. In settings.py:: DDF_DEFAULT_DATA_FIXTURE = 'static_sequential' In the test file:: instance = G(MyModel) assert instance.integerfield_unique == 1 assert instance.integerfield_notunique == 2 instance = G(MyModel) assert instance.integerfield_unique == 2 assert instance.integerfield_notunique == 2 # ... Random Data Fixture =============================================================================== Useful to use in python shells. In shell you may want to do some manual tests, and DDF may help you to generate models too. If you are using shell with a not-in-memory database, you may have problems with *SequentialDataFixture* because the sequence will be reset every time you close the shell, but the data already generated is persisted. It is dangerous to use this data fixture in a test suite because it can produce erratic tests. For example, depending on the quantity of tests and *max_length* of a *CharField*, there is a high probability to generate an identical value which will result in invalid data for fields with *unique=True*. In settings.py:: DDF_DEFAULT_DATA_FIXTURE = 'random' In the test file:: instance = G(MyModel) assert instance.integerfield_a is not None assert instance.charfield_b is not None assert instance.booleanfield in [False, True] assert instance.nullbooleanfield in [None, False, True] # ... Custom Data Fixture =============================================================================== In settings.py:: DDF_DEFAULT_DATA_FIXTURE = 'path.to.your.DataFixtureClass' In the path/to/your.py file:: from django_dynamic_fixture.ddf import DataFixture class DataFixtureClass(DataFixture): # it can inherit of SequentialDataFixture, RandomDataFixture etc. def integerfield_config(self, field, key): # method name must have the format: FIELDNAME_config return 1000 # it will always return 1000 for all IntegerField In the test file:: instance = G(MyModel) assert instance.integerfield_a == 1000 assert instance.integerfield_b == 1000 # ... Custom Field Fixture =============================================================================== You can also override a field default fixture or even create a fixture for a new field using the **DDF_FIELD_FIXTURES** settings in ``settings.py``:: # https://github.com/bradjasper/django-jsonfield import json DDF_FIELD_FIXTURES = { 'jsonfield.fields.JSONCharField': {'ddf_fixture': lambda: json.dumps({'some random value': 'c'})}, 'jsonfield.fields.JSONField': {'ddf_fixture': lambda: json.dumps([1, 2, 3])}, } django-dynamic-fixture-4.0.1/docs/source/ddf.rst000066400000000000000000000216721450115615700216200ustar00rootroot00000000000000.. _ddf: Core DDF features ******************************************************************************* .. contents:: :local: Get: G =============================================================================== The ``G`` function (shortcut for the ``get`` function) is the main feature of DDF. It is useful for integration tests, for the model logic and queries to the database. It receives a model class and it will return a **valid and persisted instance** filled with dynamically generated data:: from ddf import G # or from ddf import get author = G(Author) assert author.id is not None # indicating the instance was saved assert author.name is not None # indicating a name was generated for you assert len(author.name) > 0 This facilitates writing tests and it hides all dummy data that pollutes the source code. But all **important data of the test may be explicitly defined**. This is even a good practice, because it let the test very clear and cohesive:: book = G(Book, name='The Lord of the Rings', publish_date=date(1954, 07, 29)) assert book.name == 'The Lord of the Rings' assert book.publish_date == date(1954, 07, 29) Important details: * The **id** (AutoField) is auto filled, unless you set a value to it. * if a field has **default value**, it is used by default. Unless you override it. * if a field has **choices**, the first option is selected by default. Unless you override it. New: N =============================================================================== This function ``N`` (shortcut to ``new``) is similar to ``G``, except it will NOT save the generated instance. This is good for **unit tests**, without touching the database:: from ddf import N not_saved_book = N(Book) assert not_saved_book.id is None # indicating the instance was NOT saved assert not_saved_book.name is not None # indicating a name was generated for you It can also be usefuf to **manipulate the instance before saving it**. Usually, we need that to deal with custom fields, custom validations etc. For these cases, we can use the ``persist_dependencies=True`` parameter to save internal dependencies of ``ForeignKey`` and ``OneToOneField`` fields:: book = N(Book, persist_dependencies=True) assert book.id is None # the Book was not persisted assert book.publisher.id is not None # internal dependency was persisted ps: Since the instance does not have an ID, it can NOT insert instances in ``ManyToManyField`` fields. So, be aware to use the ``G`` function for these cases. Fixture: F =============================================================================== DDF also allows you **customise recursively through relationship fields** ``ForeignKey``, ``OneToOneField`` and ``ManyToManyField``) using the ``F`` function (shortcut for ``fixture``). ForeignKey and OneToOneField ------------------------------------------------------------------------------- To customise relationships, you can use the ``F`` function or the Django look up syntax:: from ddf import G, F book = G(Book, author=F(name='Eistein')) # or, even simpler, using the Django loop up syntax: book = G(Book, author__name='Eistein') assert book.author.name == 'Eistein' This is the equivalent to:: author = G(Author, name='Eistein') book = G(Book, author=author) assert book.author.name == 'Eistein' ``F`` function is recursible:: book = G(Book, author=F(address=F(zipcode='123456789'))) # or, even simpler, using the Django loop up syntax: book = G(Book, author__address__zipcode='123456789') assert book.author.address.zipcode == '123456789' ``F`` can be used to customize instances of a ``ManyToManyField`` too:: book = G(Book, authors=[F(name='Eistein'), F(name='Tesla')]) assert book.authors.all()[0].name == 'Eistein' assert book.authors.all()[1].name == 'Tesla' Django Look Up fields syntax (New in 1.6.1) (Fixed in 3.0.0) ------------------------------------------------------------------------------- This is an alias to ``F`` function, and it follows the Django pattern of filters that use two underlines to access internal fields of foreign keys. You can **combine** multiple parameters to create a complete customisable instance. ps: Just be careful because DDF does NOT interpret **related names** yet. It has also some limitation for many to many fields. Many to Many fields ------------------------------------------------------------------------------- DDF can add instances in ``ManyToManyFields``, but only if the instance has been persisted to the database (Django requirement). DDF handles ``ManyToManyFields`` with ``through`` table as well. It is possible to define **how many instances** will be created dynamically:: book = G(Book, authors=5) assert book.authors.all().count() == 5 It is possible to customize each instance of the ``ManyToManyField``:: book = G(Book, authors=[F(name='Eistein'), F(address__zipcode='123456789')]) assert book.authors.all().count() == 2 assert instance.authors.all()[0].name == 'Eistein' assert instance.authors.all()[1].address.zipcode == '123456789' It is possible to pass already created instances too:: author1 = G(Author) author2 = G(Author) book = G(Book, authors=[author1, author2]) assert book.authors.all().count() == 2 Or even mixed them up:: book = G(Book, authors=[F(), author1, F(), author2]) assert book.authors.all().count() == 4 Mask: M (New in 3.1.0) =============================================================================== ``M`` (shortcut for ``Mask``) is a feature that tell DDF to generate a random string using a custom mask. The mask symbols are: - ``#``: represents a number: 0-9 - ``-``: represents a upper case char: A-Z - ``_``: represents a lower case char: a-z - ``!``: escape mask symbols, inclusive itself Examples:: from ddf import G, M instance = G(Publisher, address=M(r'St. -______, ### !- -- --')) assert instance.address == 'St. Imaiden, 164 - SP BR' Copier: C (New in 1.6.0) =============================================================================== ``C`` (shortcut for ``Copier``) is a feature to copy the data of a field to another one. It is necessary to avoid cycles in the copier expression. If a cycle is found, DDF will alert the programmer the expression is invalid:: from ddf import G, C user = G(User, first_name=C('username')) assert instance.first_name == instance.username instance = G(MyModel, first_name=C('username'), username='eistein') assert instance.first_name == 'eistein' It is possible to copy values of internal relationships, but only in the **bottom-up way**:: person = G(Person, phone=C('parent.phone')) assert person.phone == person.parent.phone Teaching DDF with Lessons (shelve in 2.1.0) (New in 3.0.0) =============================================================================== Sometimes DDF can not generate a valid and persisted instance because it contains custom fields or custom validations (field or model validation). In these cases, it is possible to **teach DDF how to build a valid instance**. It is necessary to create a valid configuration and save it in an internal and global DDF library of configurations. All future instances of that model will use the saved lesson as base. In the **PyTest** **conftest.py** file or another global module that will be loaded before the test suite:: from ddf import teach teach(Author, name='Eistein') # After this command, all authors will have the name Eistein, unless it was overrided. In the test files:: from ddf import G author = G(Author) assert author.name == 'Eistein' It is possible to **override** the lessons though:: author = G(Author, name='Tesla') assert author.name == 'Tesla' It is possible to store **custom functions** of data fixtures for fields too:: zip_code_data_fixture = lambda field: 'MN {}'.format(random.randint()) teach(Address, zip_code=zip_code_data_fixture) address = G(Address) assert address.zip_code == 'MN 55416' It is possible to store **Copiers** too:: teach(Author, first_name=C('username')) author = G(Author, username='eistein') assert instance.username == 'eistein' assert instance.first_name == 'eistein' It is also possible to save custom lessons that will override the default one. But avoid having too many of them, since this will became the test suite very complex. You can have **many custom lessons** too, giving names to them:: from ddf import teach teach(Model, field_x=77) teach(Model, field_x=88, ddf_lesson='my custom lesson 1') teach(Model, field_x=99, ddf_lesson='my custom lesson 2') instance = G(Model) assert instance.field_x == 77 instance = G(Model, ddf_lesson='my custom lesson 1') assert instance.field_x == 88 instance = G(Model, ddf_lesson='my custom lesson 2') assert instance.field_x == 99 ps: Just be aware that overriding lessons is an anti-pattern and may let your test suite very hard to understand. django-dynamic-fixture-4.0.1/docs/source/fdf.rst000066400000000000000000000021551450115615700216150ustar00rootroot00000000000000.. _fdf: FileSystemDjangoTestCase (New in 1.3.0) ******************************************************************************* .. contents:: :local: *FileSystemDjangoTestCase* has a tear down method to delete all test files generated by tests, to avoid letting trash in the file system:: class MyTests(FileSystemDjangoTestCase): def test_x(self): print(dir(self)) It also has a collection of methods to simplify tests that use files, like: * create_temp_directory * remove_temp_directory * create_temp_file * create_temp_file_with_name * rename_temp_file * remove_temp_file * copy_file_to_dir * add_text_to_file * get_directory_of_the_file * get_filename * get_filepath * get_content_of_file * create_django_file_with_temp_file * create_django_file_using_file It also contains a set of assertion methods: * assertFileExists * assertFileDoesNotExists * assertDirectoryExists * assertDirectoryDoesNotExists * assertDirectoryContainsFile * assertDirectoryDoesNotContainsFile * assertFilesHaveEqualLastModificationTimestamps * assertFilesHaveNotEqualLastModificationTimestamps * assertNumberOfFiles django-dynamic-fixture-4.0.1/docs/source/index.rst000066400000000000000000000016411450115615700221640ustar00rootroot00000000000000.. DDF documentation master file, created by sphinx-quickstart on Mon Sep 1 18:20:45 2014. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to DDF's documentation! =============================== Django Dynamic Fixture (DDF) is a complete and simple library to create **dynamic model instances** for **testing purposes**. It lets you **focus on your tests**, instead of focusing on generating some dummy data which is boring and pollutes the test source code. It exists to solve the **anti-pattern** of Static Fixtures and Factory objects. Are you tired to maintain dozens of yml/json files and factory objects? .. toctree:: :maxdepth: 2 overview ddf data settings data_fixtures more patterns fdf about change_log Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` django-dynamic-fixture-4.0.1/docs/source/more.rst000066400000000000000000000105611450115615700220200ustar00rootroot00000000000000.. _more: DDF Extras ******************************************************************************* .. contents:: :local: Debugging =============================================================================== Print model instance values: P ------------------------------------------------------------------------------- Print all field values of an instance. You can also pass a list of instances or a *QuerySet*. It is useful for debug:: from django_dynamic_fixture import G, P P(G(Model)) P(G(Model, n=2)) P(Model.objects.all()) # This will print some thing like that: # :: Model my_app.MyModel (12345) # id: 12345 # field_x: 'abc' # field_y: 1 # field_z: 1 DDF compatibility checking (New in 3.0.0) ------------------------------------------------------------------------------- Before starting using DDF in your application, it is good to check its compatibility with your application models. DDF has a simple script that will look for all Django models of all installed Django applications and it will **print a report** for you:: from ddf import ddf_check_models succeeded, errors = ddf_check_models() You can also print in CSV format (using Tabs for separators):: succeeded, errors = ddf_check_models(print_csv=True) Or even save the report directly to a CSV file:: succeeded, errors = ddf_check_models(csv_filename='ddf_compatibility_report.csv') Check for a **ddf_compatibility_report.csv** file in the current directory, so you can use it better in a CSV editor. Debug Mode (New in 1.6.2) ------------------------------------------------------------------------------- Print debug log of DDF. In settings.py:: DDF_DEBUG_MODE = True # default = False In the test file:: G(MyModel, debug_mode=True) DDF Version (New in 3.0.2) ------------------------------------------------------------------------------- Type:: import ddf ddf.__version__ List of Exceptions ------------------------------------------------------------------------------- * *UnsupportedFieldError*: DynamicFixture does not support this field. * *InvalidConfigurationError*: The specified configuration for the field can not be applied or it is bugged. * *InvalidManyToManyConfigurationError*: M2M attribute configuration must be a number or a list of DynamicFixture or model instances. * *BadDataError*: The data passed to a field has some problem (not unique or invalid) or a required attribute is in ignore list. * *InvalidCopierExpressionError*: The specified expression used in a Copier is invalid. * *InvalidModelError*: Invalid Model: The class is not a model or it is abstract. Decorators (New in 1.4.0) =============================================================================== Example:: from django_dynamic_fixture.decorators import skip_for_database, only_for_database, SQLITE3, POSTGRES @skip_for_database(SQLITE3) def test_some_feature_that_use_raw_sql_not_supported_by_sqlite(self): pass @only_for_database(POSTGRES) def test_some_feature_that_use_raw_sql_specific_to_my_production_database(self): pass It is possible to pass a custom string as argument, one that would be defined in settings.DATABASES['default']['ENGINE']:: @skip_for_database('my custom driver') @only_for_database('my custom driver') Validation =============================================================================== Validate Models (New in 1.5.0) ------------------------------------------------------------------------------- This option is a flag to determine if the method *full_clean* of model instances will be called before DDF calls the save method. In settings.py:: DDF_VALIDATE_MODELS = False In the test file:: G(MyModel, validate_models=True) Signals PRE_SAVE and POST_SAVE: =============================================================================== In very special cases a signal may facilitate implementing tests with DDF, but Django signals may not be satisfactory for testing purposes because the developer does not have control of the execution order of the receivers. For this reason, DDF provides its own signals. It is possible to have only one receiver for each model, to avoid anti-patterns:: from django_dynamic_fixture import PRE_SAVE, POST_SAVE def callback_function(instance): pass # do something PRE_SAVE(MyModel, callback_function) POST_SAVE(MyModel, callback_function) django-dynamic-fixture-4.0.1/docs/source/overview.rst000066400000000000000000000153161450115615700227270ustar00rootroot00000000000000.. _overview: Getting Started ******************************************************************************* .. contents:: :local: Basic Example of Usage =============================================================================== Models sample (`models.py`):: from django.db import models class Author(models.Model): name = models.CharField(max_length=255) class Book(models.Model): name = models.CharField(max_length=255) authors = models.ManyToManyField(Author) @staticmethod def search_by_author(author_name): return Book.objects.filter(authors__name=author_name) **PyTest** example (`tests/test_books.py`):: from ddf import G def test_search_book_by_author(): author1 = G(Author) author2 = G(Author) book1 = G(Book, authors=[author1]) book2 = G(Book, authors=[author2]) books = Book.objects.search_by_author(author1.name) assert book1 in books assert book2 not in books **Django TestCase** example (`tests/test_books.py`):: from django.test import TestCase from ddf import G class SearchingBooks(TestCase): def test_search_book_by_author(self): author1 = G(Author) author2 = G(Author) book1 = G(Book, authors=[author1]) book2 = G(Book, authors=[author2]) books = Book.objects.search_by_author(author1.name) self.assertTrue(book1 in books) self.assertTrue(book2 not in books) Installation =============================================================================== :: pip install django-dynamic-fixture or:: 1. Download zip file 2. Extract it 3. Execute in the extracted directory: python setup.py install Development version ------------------------------------------------------------------------------- :: pip install -e git+git@github.com:paulocheque/django-dynamic-fixture.git#egg=django-dynamic-fixture requirements.txt ------------------------------------------------------------------------------- :: django-dynamic-fixture== # or use the development version git+git://github.com/paulocheque/django-dynamic-fixture.git#egg=django-dynamic-fixture Upgrade ------------------------------------------------------------------------------- :: pip install django-dynamic-fixture --upgrade --no-deps Support and Compatibility =============================================================================== +---------------------------------------------------------+ | DDF current support | +================+=================+======================+ | Python 3.11 | Django 4.x.x | DDF 4.*.* - Aug 2023 | +----------------+-----------------+----------------------+ | Python 3.10 | Django 4.x.x | DDF 4.*.* - Aug 2023 | +----------------+-----------------+----------------------+ | Python 3.9 | Django 4.x.x | DDF 4.*.* - Aug 2023 | +----------------+-----------------+----------------------+ | Python 3.8 | Django 4.x.x | DDF 4.*.* - Aug 2023 | +----------------+-----------------+----------------------+ For old versions of Python or Django, use old versions of DDF too: +---------------------------------------------------------+ | DDF old support | +================+=================+======================+ | Python 3.8 | Django 3.x.x | DDF 3.*.* - Jan 2020 | +----------------+-----------------+----------------------+ | Python 3.7 | Django 3.x.x | DDF 3.*.* - Jan 2020 | +----------------+-----------------+----------------------+ | Python 3.6 | Django 3.x.x | DDF 3.*.* - Jan 2020 | +----------------+-----------------+----------------------+ | Python > 3.5 | Django 2.x.x | DDF 3.*.* - Jan 2020 | +----------------+-----------------+----------------------+ | Python 3.3 | Django < 1.11.x | DDF 2.0.* - Dec 2017 | +----------------+-----------------+----------------------+ | Python 2.7 | Django 1.11.x | DDF 3.*.* - Jan 2020 | +----------------+-----------------+----------------------+ | Python <= 2.7 | Django < 1.11.x | DDF 2.0.* - Dec 2017 | +----------------+-----------------+----------------------+ List of features =============================================================================== * Highly customizable: you can **customize fields recursively** * Deals with **unique=True** * Deals with **cyclic dependencies** (including self references) * Deals with **many to many relationship** (common M2M or M2M with additional data, i.e. **through='table'**) * Deals with **custom fields** (especially if the custom field inherits from a django field) * Support for **parallel tests** * Deals with **auto calculated** attributes * It is **easy to debug errors** Motivation =============================================================================== * It is a terrible practice to use **static data** in tests (yml/json/sql files). * It is very hard to maintain lots of **Factory objects**. * Creating fixtures for each model is boring and it produces a lot of **replicated code**. * It is a bad idea to use uncontrolled data in tests, like bizarre random data. Comparison with other tools =============================================================================== The DDF was created in a context of a project with a **very complex design** with many **cyclic dependencies**. In that context, no available tool was satisfactory. Or we stumbled in some **infinite loop** or some bug caused by a **custom field**. For these reasons, the tests started to fail and it was very hard to understand why. Another thing, the DDF was planned to have a **lean and clean syntax**. We believe that automated tests must be developed quickly with the **minimum overhead**. For that reason we are in favor of **less verbose approach**, except in the documentation ;) Also, DDF is flexible, since it is possible to customize the entire data generation or by field. * Either they are incomplete, or bugged or it produces erratic tests, because they use random and uncontrolled data. * The syntax of others tools is too verbose, which pollutes the tests. * Complete, lean and practice documentation. * It is hard to debug tests with another tools. * List of other tools: or * The core of the tool is the algorithm, it is not the data generation as all other tools. This means you can change the data generation logic. Plus: * **PyTest** compatible * **Command** to count how many queries are executed to save any kind of model instance * **FileSystemDjangoTestCase** that facilitates to create tests for features that use filesystem. django-dynamic-fixture-4.0.1/docs/source/patterns.rst000066400000000000000000000032111450115615700227100ustar00rootroot00000000000000.. _patterns: Patterns ******************************************************************************* .. contents:: :local: Patterns =============================================================================== * Use *N* function for unit tests (not integration tests) for the main model. * Use *G* function instead of *N* for shelving configurations. It helps to identify errors. * Use *ignore_fields* option to deal with fields filled by listeners. * Use custom values for unsupported fields. * Use default lesson for unsupported fields with a custom function that generated data. * Use *fill_nullable_fields* for unsupported nullable fields. * Use *fk_min_depth* option to test trees. * [Automated Test Patterns](http://www.teses.usp.br/teses/disponiveis/45/45134/tde-02042012-120707/pt-br.php) (in Portuguese) Anti-Patterns =============================================================================== * Using auto generated data in an assertion method. * Shelving a static value for a field with *unique=True*. Raise an error. * Overriding a lesson *Copier* with None. * Using *Copier* to fix a bad design of the code. * Too many named lessons configurations. * Mix data fixture algorithms in the same test suite. * Use default and custom lessons to avoid fixing a messy architecture. * Extensive use of global set up: setup suite (from DDF), setup module and setup class (from unittest2). A good automated test =============================================================================== * Simple * Legible * Repeatable * Independent * Isolated * Useful * Unique * Accurate * Professional * Fastdjango-dynamic-fixture-4.0.1/docs/source/settings.rst000066400000000000000000000045271450115615700227230ustar00rootroot00000000000000.. _settings: DDF Settings =============================================================================== .. contents:: :local: .. role:: python(code) :language: python Global Settings ------------------------------------------------------------------------------- You can configure DDF in ``settings.py`` file. You can also override the global config per instance creation when necessary. * **DDF_FILL_NULLABLE_FIELDS** (Default = False): DDF can fill nullable fields (``null=True``) with None or some data:: # SomeModel(models.Model): nullable_field = Field(null=True) G(SomeModel).nullable_field is None # True if DDF_FILL_NULLABLE_FIELDS is True G(SomeModel).nullable_field is None # False if DDF_FILL_NULLABLE_FIELDS is False # You can override the global config for one case: assert G(Model, fill_nullable_fields=False).nullable_field is None assert G(Model, fill_nullable_fields=True).nullable_field is not None * **DDF_VALIDATE_MODELS** (Default = False): DDF will call ``model.full_clean()`` method before saving to the database:: # You can override the global config for one case: G(Model, validate_models=True) G(Model, validate_models=False) * **DDF_FIELD_FIXTURES** (Default = {}) (new in 1.8.0): Dictionary where the key is the full qualified name of the field and the value is a function without parameters that returns a value:: DDF_FIELD_FIXTURES = {'path.to.your.Field': lambda: random.randint(0, 10) } * **DDF_FK_MIN_DEPTH** (Default = 0): For models with non required foreign keys (FKs with `null=True`), like FKs to itself (``ForeignKey('self')``), cyclic dependencies or even optional FKs, DDF will avoid infinite loops because it stops creating objects indefinitely, because it will stop after the min depth was achieved.:: # You can override the global config for one case: G(Model, fk_min_depth=5) > Incompatibility warning: Before DDF 3.0.3, DDF handled FK cycles instead of FK depth, through the removed properties `DDF_NUMBER_OF_LAPS` and `number_of_laps`. * **DDF_DEBUG_MODE** (Default = False): To show some DDF logs:: # You can override the global config for one case: G(Model, debug_mode=True) G(Model, debug_mode=False) * **DDF_SHELL_MODE** (Default = False): To disable some DDF warnings so DDF can be used better in Python shell: to populate the DB, for example. django-dynamic-fixture-4.0.1/manage.py000077500000000000000000000012161450115615700176760ustar00rootroot00000000000000#!/usr/bin/env python try: import settings_sqlite # Assumed to be in the same directory. except ImportError: import sys sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) sys.exit(1) if __name__ == "__main__": import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings_sqlite") from django.core.management import execute_from_command_line execute_from_command_line() django-dynamic-fixture-4.0.1/pytest.ini000066400000000000000000000002051450115615700201170ustar00rootroot00000000000000[pytest] DJANGO_SETTINGS_MODULE = settings_sqlite addopts = --pyargs --create-db --reuse-db --no-migrations python_files = test_*.py django-dynamic-fixture-4.0.1/queries/000077500000000000000000000000001450115615700175465ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/queries/__init__.py000066400000000000000000000000001450115615700216450ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/queries/count_queries_on_save.py000066400000000000000000000046101450115615700245200ustar00rootroot00000000000000 from django_dynamic_fixture import new from django.conf import settings from django.db import connection from django import db from django_dynamic_fixture.django_helper import get_models_of_an_app, is_model_managed, get_unique_model_name, get_apps class Report: def __init__(self): self.data = [] self.errors = [] def add_record(self, app, model, queries_insert, queries_update): self.data.append((app, model, queries_insert, queries_update)) def add_error(self, msg): self.errors.append(msg) def export_csv(self, order_by_quantity_queries=False): if order_by_quantity_queries: self.data.sort(key=lambda t: t[2], reverse=True) print('APP.MODEL;QUERIES ON INSERT;QUERIES ON UPDATE') for app, model, queries_insert, queries_update in self.data: print('%s;%s;%s' % (get_unique_model_name(model), queries_insert, queries_update)) for err in self.errors: print(err) class CountQueriesOnSave: def __init__(self): self.report = Report() def count_queries_for_model(self, app, model): try: model_instance = new(model, print_errors=False) except Exception as e: self.report.add_error('- Could not prepare %s: %s' % (get_unique_model_name(model), str(e))) return db.reset_queries() try: model_instance.save() except Exception as e: self.report.add_error('- Could not insert %s: %s' % (get_unique_model_name(model), str(e))) return queries_insert = len(connection.queries) db.reset_queries() try: model_instance.save() except Exception as e: self.report.add_error('- Could not update %s: %s' % (get_unique_model_name(model), str(e))) return queries_update = len(connection.queries) self.report.add_record(app, model, queries_insert, queries_update) def execute(self, app_labels=[], exclude_app_labels=[]): settings.DEBUG = True apps = get_apps(application_labels=app_labels, exclude_application_labels=exclude_app_labels) for app in apps: models = get_models_of_an_app(app) for model in models: if not is_model_managed(model): continue self.count_queries_for_model(app, model) return self.report django-dynamic-fixture-4.0.1/queries/management/000077500000000000000000000000001450115615700216625ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/queries/management/__init__.py000066400000000000000000000000011450115615700237620ustar00rootroot00000000000000 django-dynamic-fixture-4.0.1/queries/management/commands/000077500000000000000000000000001450115615700234635ustar00rootroot00000000000000django-dynamic-fixture-4.0.1/queries/management/commands/__init__.py000066400000000000000000000000011450115615700255630ustar00rootroot00000000000000 django-dynamic-fixture-4.0.1/queries/management/commands/count_queries_on_save.py000066400000000000000000000024071450115615700304370ustar00rootroot00000000000000 from optparse import make_option from django.core.management.base import AppCommand from queries.count_queries_on_save import CountQueriesOnSave class Command(AppCommand): help = """ Default Usage: manage.py count_queries_on_save manage.py count_queries_on_save --settings=my_settings = Specifying apps: manage.py count_queries_on_save [APP_NAMES]+ Usage: manage.py count_queries_on_save app1 app2 manage.py count_queries_on_save app1 app2 = Skipping apps: manage.py count_queries_on_save --skip=[APP_NAME,]+ Usage: manage.py count_queries_on_save --skip=app1 manage.py count_queries_on_save --skip=app1,app2 manage.py count_queries_on_save app1 app2 --skip=app2 """ args = ' --skip=APP1,APP2' option_list = AppCommand.option_list + ( make_option('--skip', '-s', dest='skip-apps', default='', help='Skip applications. Separate application labels by commas.'), ) def handle(self, *args, **options): script = CountQueriesOnSave() app_labels = args exclude_app_labels = options['skip-apps'].split(',') report = script.execute(app_labels, exclude_app_labels) report.export_csv(order_by_quantity_queries=True) django-dynamic-fixture-4.0.1/requirements-dev.txt000066400000000000000000000005261450115615700221340ustar00rootroot00000000000000# Build/Package/Publish build twine # Tests and Coverage pytest pytest-django pytest-xdist # Run tests in parallel pytest-cov tox # For the CI psycopg2-binary #psycopg2==2.8.4 # For tests with Postgres fields #or psycopg2cffi==2.7.5 # For pypy # Code flake8 pyflakes pylint ruff pre-commit # Docs sphinx sphinx_rtd_theme sphinx-rtd-theme django-dynamic-fixture-4.0.1/requirements.txt000066400000000000000000000003051450115615700213530ustar00rootroot00000000000000# Database Drivers #mysql-python #psycopg2 # not pypy compatible #psycopg2cffi # pypy2+ compatible # DjangoGEO geopy # 3rd party libraries # jsonfield>=2.1.1 django-json-field django-polymorphic django-dynamic-fixture-4.0.1/settings_ddf.py000066400000000000000000000025351450115615700211250ustar00rootroot00000000000000from distutils.version import StrictVersion import django DJANGO_VERSION = django.get_version()[0:3] DEBUG = True IMPORT_DDF_MODELS = True SECRET_KEY = 'ddf-secret-key' ALLOWED_HOSTS = ['*'] # Since Django 1.11, it is verified when running tests INSTALLED_APPS = () INSTALLED_APPS += ( 'queries', 'django_dynamic_fixture', 'django.contrib.contenttypes', ) try: import polymorphic INSTALLED_APPS += ('polymorphic',) except ImportError: pass # EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' # EMAIL_FILE_PATH = '/tmp/invest-messages' # change this to a proper location EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # python manage.py test --with-coverage --cover-inclusive --cover-html --cover-package=django_dynamic_fixture.* --with-queries --with-ddf-setup # To avoid warnings MIDDLEWARE_CLASSES = () # Example of DDF plugins for Custom fields import json DDF_FIELD_FIXTURES = { 'django_dynamic_fixture.models_test.CustomDjangoField': {'ddf_fixture': lambda: 123456789}, 'django_dynamic_fixture.models_test.CustomDjangoField2': lambda: 987654321, # https://github.com/bradjasper/django-jsonfield 'jsonfield.fields.JSONCharField': {'ddf_fixture': lambda: json.dumps({'some random value': 'c'})}, 'jsonfield.fields.JSONField': {'ddf_fixture': lambda: json.dumps([1, 2, 3])}, } django-dynamic-fixture-4.0.1/settings_mysql.py000066400000000000000000000006321450115615700215310ustar00rootroot00000000000000from settings_ddf import * DDF_TEST_GEODJANGO = True # MySQL DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'ddf', 'USER': 'paulocheque', # please, change this if you want to run tests in your machine 'PASSWORD': '', 'HOST': 'localhost', 'PORT': 3306, } } if DDF_TEST_GEODJANGO: INSTALLED_APPS += ('django.contrib.gis',) django-dynamic-fixture-4.0.1/settings_postgres.py000066400000000000000000000013741450115615700222360ustar00rootroot00000000000000import os from settings_ddf import * DDF_TEST_GEODJANGO = True # Postgres and PostGis # > psql -d ddf -c "CREATE EXTENSION postgis;" # > psql -d ddf -c "select postgis_lib_version();" DATABASES = { 'default': { # Postgis supports all Django features # createdb ddf 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'HOST': os.getenv('POSTGRES_HOST', 'localhost'), 'PORT': os.getenv('POSTGRES_PORT', 5432), 'NAME': os.getenv('POSTGRES_DB', 'ddf'), 'USER': os.getenv('POSTGRES_USER', 'ddf_user'), # please, change this if you want to run tests in your machine 'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'ddf_pass'), } } if DDF_TEST_GEODJANGO: INSTALLED_APPS += ('django.contrib.gis',) django-dynamic-fixture-4.0.1/settings_sqlite.py000066400000000000000000000005761450115615700216740ustar00rootroot00000000000000from settings_ddf import * DDF_TEST_GEODJANGO = False # SQlite and SpatialLite # brew install spatialite-tools # brew install gdal SPATIALITE_LIBRARY_PATH = '/usr/local/lib/mod_spatialite.dylib' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } if DDF_TEST_GEODJANGO: INSTALLED_APPS += ('django.contrib.gis',) django-dynamic-fixture-4.0.1/setup.py000066400000000000000000000024271450115615700176100ustar00rootroot00000000000000from pathlib import Path from setuptools import setup, find_packages VERSION = '4.0.1' tests_require = [] install_requires = [] this_directory = Path(__file__).parent long_description = (this_directory / 'README.md').read_text() setup(name='django-dynamic-fixture', url='https://github.com/paulocheque/django-dynamic-fixture', author="paulocheque", author_email='paulocheque@gmail.com', keywords='python django testing fixture', description='A full library to create dynamic model instances for testing purposes.', long_description_content_type='text/markdown', long_description=long_description, license='MIT', classifiers=[ 'Framework :: Django', 'Operating System :: OS Independent', 'Topic :: Software Development', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: Implementation :: PyPy', ], version=VERSION, install_requires=install_requires, tests_require=tests_require, test_suite='pytest', extras_require={'test': tests_require}, packages=find_packages(), ) django-dynamic-fixture-4.0.1/tox.ini000066400000000000000000000010411450115615700174000ustar00rootroot00000000000000[tox] envlist = py38-django40 py38-django41 py38-django42 py39-django40 py39-django41 py39-django42 py310-django40 py310-django41 py310-django42 py311-django40 py311-django41 py311-django42 [testenv] whitelist_externals = pytest allowlist_externals = pytest deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements-dev.txt django40: Django>=4.0,<4.1 django41: Django>=4.1,<4.2 django42: Django>=4.2,<4.3 commands = pytest -n 3 --create-db --reuse-db --no-migrations