Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model not seting default values after building a factory #128

Closed
eloi-martinez-qida opened this issue Nov 28, 2022 · 16 comments · Fixed by #133
Closed

Model not seting default values after building a factory #128

eloi-martinez-qida opened this issue Nov 28, 2022 · 16 comments · Fixed by #133
Assignees
Labels
bug Something isn't working

Comments

@eloi-martinez-qida
Copy link

eloi-martinez-qida commented Nov 28, 2022

Creating a model after building a factory of the model rises a validation error of the default values not being defined.

Example:

class TestModel(ormar.Model):
    class Meta(ormar.ModelMeta):
        database = database
        metadata = meta

    id: UUID4 = ormar.UUID(primary_key=True, default=uuid4)
    text: str = ormar.Text()
    created_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)


class TestModelFactory(OrmarModelFactory):
    __model__ = TestModel


def test_test_model():
    TestModelFactory.build()
    TestModel(text='qwerty')

results in:

>               raise validation_error
E               pydantic.error_wrappers.ValidationError: 2 validation errors for TestModel
E               id
E                 field required (type=value_error.missing)
E               created_date
E                 field required (type=value_error.missing)

Removing TestModelFactory.build() works as intended.

The problem persists if the factory is called from one test and the instancing in the model in another one.
Ex.

def test_test_model_build():
    TestModelFactory.build()

def test_test_model():
    TestModel(text='qwerty')
@jtraub
Copy link
Contributor

jtraub commented Nov 28, 2022

@eloi-martinez-qida It would help a lot if you provide a minimal example (py file + list of dependencies with their version) that people can run.

For example, I have no experience with ormar whatsoever and it took me like 20 minutes to read ormar docs and find missing pieces in your code but it works just fine for me. I had to modify it (e.g. rename UUID4 to UUID and BaseMeta to Meta) too.

Since I have no clue how ormar works maybe I messed up the your code somehow without even knowing it.

@eloi-martinez-qida
Copy link
Author

eloi-martinez-qida commented Nov 28, 2022

@jtraub yes, sorry for missing that, the problem persists using a pydantic BaseModel and makes the example simpler.

import datetime
from uuid import uuid4

import ormar
from pydantic import UUID4, BaseModel
from pydantic_factories import OrmarModelFactory


class TestModel(BaseModel):
    id: UUID4 = ormar.UUID(primary_key=True, default=uuid4)
    text: str = ormar.Text()
    created_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)


class TestModelFactory(OrmarModelFactory):
    __model__ = TestModel


def test_test_model():
    TestModelFactory.build()
    TestModel(text='qwerty')

Versions from pip freeze:

aiosqlite==0.17.0
alembic==1.8.1
anyio==3.6.2
apprise==1.1.0
asgi-lifespan==2.0.0
astor==0.8.1
asyncpg==0.27.0
attrs==22.1.0
autoflake==1.7.8
bandit==1.7.4
bcrypt==4.0.1
black==22.10.0
CacheControl==0.12.11
cachetools==5.2.0
cachy==0.3.0
certifi==2022.9.24
cffi==1.15.1
cfgv==3.3.1
charset-normalizer==2.1.1
cleo==1.0.0a5
click==8.1.3
cloudpickle==2.2.0
colorama==0.4.6
commonmark==0.9.1
coolname==2.0.0
coverage==6.5.0
crashtest==0.3.1
croniter==1.3.7
cryptography==38.0.3
darglint==1.8.1
databases==0.6.1
distlib==0.3.6
docker==6.0.1
docutils==0.19
dulwich==0.20.50
ecdsa==0.18.0
eradicate==2.1.0
Faker==15.3.3
fastapi==0.88.0
filelock==3.8.0
flake8==4.0.1
flake8-bandit==3.0.0
flake8-broken-line==0.5.0
flake8-bugbear==22.10.27
flake8-commas==2.1.0
flake8-comprehensions==3.10.1
flake8-debugger==4.1.2
flake8-docstrings==1.6.0
flake8-eradicate==1.4.0
flake8-isort==4.2.0
flake8-polyfill==1.0.2
flake8-quotes==3.3.1
flake8-rst-docstrings==0.2.7
flake8-string-format==0.3.0
freezegun==1.2.2
fsspec==2022.11.0
gitdb==4.0.9
GitPython==3.1.29
google-auth==2.14.1
greenlet==2.0.1
griffe==0.24.0
h11==0.12.0
h2==4.1.0
hpack==4.0.0
html5lib==1.1
httpcore==0.15.0
httptools==0.5.0
httpx==0.23.1
hyperframe==6.0.1
identify==2.5.8
idna==3.4
importlib-metadata==5.0.0
iniconfig==1.1.1
isort==5.10.1
jaraco.classes==3.2.3
jeepney==0.8.0
jsonpatch==1.32
jsonpointer==2.3
jsonschema==4.17.0
keyring==23.11.0
kubernetes==25.3.0
lockfile==0.12.2
Mako==1.2.3
Markdown==3.4.1
MarkupSafe==2.1.1
mccabe==0.6.1
more-itertools==9.0.0
msgpack==1.0.4
multidict==6.0.2
mypy==0.991
mypy-extensions==0.4.3
nodeenv==1.7.0
oauthlib==3.2.2
orjson==3.8.1
ormar==0.12.0
packaging==21.3
paramiko==2.12.0
pathspec==0.10.2
pbr==5.11.0
pendulum==2.1.2
pep8-naming==0.13.2
pexpect==4.8.0
pkginfo==1.8.3
platformdirs==2.5.4
pluggy==1.0.0
poetry==1.2.2
poetry-core==1.3.2
poetry-plugin-export==1.2.0
pre-commit==2.20.0
prefect==2.6.9
psycopg2-binary==2.9.5
ptyprocess==0.7.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycodestyle==2.8.0
pycparser==2.21
pydantic==1.10.2
pydantic-factories==1.16.0
pydocstyle==6.1.1
pyflakes==2.4.0
Pygments==2.13.0
pylev==1.4.0
PyNaCl==1.5.0
pyparsing==3.0.9
pyrsistent==0.19.2
pytest==7.2.0
pytest-asyncio==0.20.2
pytest-cov==4.0.0
pytest-env==0.8.1
pytest-sftpserver==1.3.0
python-dateutil==2.8.2
python-dotenv==0.21.0
python-jose==3.3.0
python-slugify==6.1.2
pytz==2022.6
pytzdata==2020.1
PyYAML==6.0
readchar==4.0.3
requests==2.28.1
requests-oauthlib==1.3.1
requests-toolbelt==0.9.1
restructuredtext-lint==1.4.0
rfc3986==1.5.0
rich==12.6.0
rsa==4.9
SecretStorage==3.3.3
shellingham==1.5.0
six==1.16.0
smmap==5.0.0
sniffio==1.3.0
snowballstemmer==2.2.0
SQLAlchemy==1.4.41
starlette==0.22.0
stevedore==4.1.1
text-unidecode==1.3
tokenize-rt==5.0.0
toml==0.10.2
tomlkit==0.11.6
typer==0.7.0
types-cryptography==3.3.23.2
types-paramiko==2.12.0.1
typing_extensions==4.4.0
ujson==5.5.0
urllib3==1.26.12
uvicorn==0.20.0
uvloop==0.17.0
-e /app/src
virtualenv==20.16.7
watchfiles==0.18.1
webencodings==0.5.1
websocket-client==1.4.2
websockets==10.4
wemake-python-styleguide==0.17.0
yarl==1.8.1
yesqa==1.4.0
zipp==3.10.0

@eloi-martinez-qida
Copy link
Author

The base project is this template with ormar: https://github.com/s3rius/FastAPI-template

@jtraub
Copy link
Contributor

jtraub commented Nov 28, 2022

Thanks a lot. I can reproduce the problem now.

@jtraub jtraub added the bug Something isn't working label Nov 28, 2022
@eloi-martinez-qida
Copy link
Author

Thanks you for the fast response and pointing out what i was lacking

@jtraub
Copy link
Contributor

jtraub commented Nov 29, 2022

@eloi-martinez-qida could you please install pydantic-factories from this branch - https://github.com/starlite-api/pydantic-factories/tree/fix_ormar and check if your actual code still fails.

I noticed that for some reason Pydantic sets default_factory to None when required is modified but I don't understand why yet. So to fix this I decided to do a conditional deepcopy but I am not sure if that would be enough and probably we would have to do an unconditional deepcopy

@eloi-martinez-qida
Copy link
Author

eloi-martinez-qida commented Nov 29, 2022

thanks, now id and created_date works as expected, but for some reason, state does not (it's an Enum, sorry for not including it in the example, i was trying to make it as simple as possible)

I'm appending an example for you to test:

import datetime
from enum import Enum
from uuid import uuid4

import ormar
import sqlalchemy
from databases import Database
from pydantic import UUID4
from pydantic_factories import OrmarModelFactory


class TestState(Enum):
    NEW = 'NEW'
    OK = 'OK'
    ERROR = 'ERROR'


class TestModel(ormar.Model):
    class Meta:
        database = Database('postgresql://')
        metadata = sqlalchemy.MetaData()
    id: UUID4 = ormar.UUID(primary_key=True, default=uuid4)
    text: str = ormar.Text()
    created_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)
    state: Enum = ormar.Enum(enum_class=TestState, default=TestState.NEW)


class TestModelFactory(OrmarModelFactory):
    __model__ = TestModel


def test_test_model():
    TestModelFactory.build()
    TestModel(text='qwerty')

@jtraub
Copy link
Contributor

jtraub commented Nov 29, 2022

@eloi-martinez-qida that is quite interesting behaviour here.

I receive raise IndexError('Cannot choose from an empty sequence'). Do you observer the same error with enums?
Did it work before?

@eloi-martinez-qida
Copy link
Author

@jtraub Are you inheriting TestModel from ormar.Model or BaseModel?
When inheriting from BaseModel i get the same error than you.
When inheriting form ormar.Model i get:

>               raise validation_error
E               pydantic.error_wrappers.ValidationError: 1 validation error for TestModel
E               state
E                 field required (type=value_error.missing)

When not building the Factory, it runs as expected.
I should also add that i'm running python 3.11 (FROM python:3.11.0-slim-bullseye) but i think you already know that.

@eloi-martinez-qida
Copy link
Author

@jtraub any clue why this could happen? maybe something else needs to be deep_copied?

@eloi-martinez-qida
Copy link
Author

@jtraub can you reproduce the bug?

@jtraub
Copy link
Contributor

jtraub commented Dec 8, 2022

@eloi-martinez-qida hi, yes. I can reproduce it. Actually, I am looking into it right now.

I had something else happening so I was offline for the most of the last week.

@jtraub jtraub self-assigned this Dec 11, 2022
@jtraub
Copy link
Contributor

jtraub commented Dec 13, 2022

@eloi-martinez-qida could you please check again by installing pydantc-factories from the same branch?

@eloi-martinez-qida
Copy link
Author

@jtraub yes, that fixes the enum problem, thanks!

Now i have found a problem with nullable fields, sorry for not caching it beforehand
To replicate set text to:
text: str = ormar.Text(nullable=True)
and
DemoTestModel()
you will get:
E pydantic.error_wrappers.ValidationError: 1 validation error for DemoTestModel
E text
E field required (type=value_error.missing)
(https://collerek.github.io/ormar/fields/common-parameters/#nullable)

I also found some tests that are failing with the presence of other tests (might not be related, i have to look into it more)

@jtraub
Copy link
Contributor

jtraub commented Dec 14, 2022

@eloi-martinez-qida do the tests again, please :-) If this won't help I will simply switch to unconditional deep copy. This would impact performance a bit but at least it should work in all cases.

@eloi-martinez-qida
Copy link
Author

@jtraub Yes, sorry for the delay, now everything works as intended, thanks and nice job!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants