art049 / odmantic

Sync and Async ODM (Object Document Mapper) for MongoDB based on python type hints
http://art049.github.io/odmantic
ISC License
1.07k stars 93 forks source link

`key_name` doesn't work when field is `id` #468

Open z0z0r4 opened 6 months ago

z0z0r4 commented 6 months ago

Bug

key_name doesn't work when id

Current Behavior

class Project(Model):
    id: str = Field(unique=True, key_name="project_id")

TypeError: can't automatically generate a primary field since an 'id' field already exists

Traceback (most recent call last):
  File "D:\Python\Python312\Lib\runpy.py", line 198, in _run_module_as_main
    return _run_code(code, main_globals, None,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Python\Python312\Lib\runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "c:\Users\DELL\.vscode\extensions\ms-python.debugpy-2024.4.0-win32-x64\bundled\libs\debugpy\adapter/../..\debugpy\launcher/../..\debugpy\__main__.py", line 39, in <module>
    cli.main()
  File "c:\Users\DELL\.vscode\extensions\ms-python.debugpy-2024.4.0-win32-x64\bundled\libs\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 430, in main
    run()
  File "c:\Users\DELL\.vscode\extensions\ms-python.debugpy-2024.4.0-win32-x64\bundled\libs\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "c:\Users\DELL\.vscode\extensions\ms-python.debugpy-2024.4.0-win32-x64\bundled\libs\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\DELL\.vscode\extensions\ms-python.debugpy-2024.4.0-win32-x64\bundled\libs\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "c:\Users\DELL\.vscode\extensions\ms-python.debugpy-2024.4.0-win32-x64\bundled\libs\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "D:\projects\mcim-backend\test.py", line 1, in <module>
    from main import *
  File "D:\projects\mcim-backend\main.py", line 3, in <module>
    from config import redis_broker
  File "D:\projects\mcim-backend\config.py", line 9, in <module>
    from models.modrinth import Project, Version, File
  File "D:\projects\mcim-backend\models\modrinth.py", line 16, in <module>
    class Project(Model):
  File "d:\projects\mcim-backend\.venv\Lib\site-packages\odmantic\model.py", line 463, in __new__
    raise TypeError(
TypeError: can't automatically generate a primary field since an 'id' field already exists

Expected behavior

work as proid successfully

class Project(Model):
    proid: str = Field(unique=True, key_name="project_id")

Environment

D:\projects\mcim-backend\.venv\Lib\site-packages\pydantic\_migration.py:283: UserWarning: `pydantic.utils:version_info` has been moved to `pydantic.version:version_info`.
  warnings.warn(f'`{import_path}` has been moved to `{new_location}`.')
             pydantic version: 2.7.1
        pydantic-core version: 2.18.2
          pydantic-core build: profile=release pgo=true
                 install path: D:\projects\mcim-backend\.venv\Lib\site-packages\pydantic
               python version: 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)]       
                     platform: Windows-11-10.0.22631-SP0
             related packages: typing_extensions-4.11.0
                       commit: unknown

Additional context

check key_word plz

            if primary_field is None:
                if "id" in odm_fields:
                    # FIX: find that field in the odm_fields and check its `key_name`
                    raise TypeError(
                        "can't automatically generate a primary field since an 'id' "
                        "field already exists"
                    )
                primary_field = "id"
                odm_fields["id"] = ODMField(
                    primary_field=True, key_name="_id", model_config=config
                )
                namespace["id"] = PDField(default_factory=ObjectId)
                namespace["__annotations__"]["id"] = ObjectId

image

z0z0r4 commented 6 months ago
            if primary_field is None:
                if "id" in odm_fields:
                    if odm_fields["id"].key_name in ("id", None):
                        raise TypeError(
                            "can't automatically generate a primary field since an 'id' "
                            "field already exists"
                        )

But another error


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\projects\mcim-backend\.venv\Lib\site-packages\odmantic\model.py", line 543, in __init__
    super().__init__(**data)
  File "D:\projects\mcim-backend\.venv\Lib\site-packages\pydantic\main.py", line 176, in __init__
    self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Project
id.is-instance[ObjectId]
  Input should be an instance of ObjectId [type=is_instance_of, input_value='A', input_type=str]
    For further information visit https://errors.pydantic.dev/2.7/v/is_instance_of
id.chain[union[str,bytes],function-plain[validate_from_string_or_bytes()]]
  Value error, Invalid ObjectId [type=value_error, input_value='A', input_type=str]
    For further information visit https://errors.pydantic.dev/2.7/v/value_error
z0z0r4 commented 6 months ago

It seems that no way to solve it?

d3cryptofc commented 3 weeks ago
class Project(Model):
    proid: str = Field(unique=True, key_name="project_id")

Every document needs to have a UUID, what you are doing is using the name 'id' to store something else, but this is a reserved name for the primary field.

You will have to define another field for the primary field:

class Project(Model):
  uuid: ObjectId = Field(primary_field=True, default_factory=ObjectId)
  id: int = Field(key_name='project_id')