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

[BUG] Updating documents with a frozen BaseModel as field raises TypeError #599

Closed
Mark90 opened this issue Jun 22, 2023 · 6 comments
Closed

Comments

@Mark90
Copy link

Mark90 commented Jun 22, 2023

Describe the bug
Relating a document X to document Y using Link[], with document Y having a pydantic.BaseModel field that has class Config: frozen = True, will trigger the error TypError: <field> is immutable in Pydantic.

To Reproduce

edit please check my comment below, there is an easier way to reproduce it.

class PersonDetails(BaseModel):
    ssn: int
    dob: str

    class Config:
        frozen = True

class Person(Document):
    first_name: str
    last_name: str
    details: PersonDetails

class House(Document):
    name: str
    people: list[Link[Person]]


await init_beanie(database=db, document_models=[House, Person])

person = Person(first_name="john", last_name="doe", details=PersonDetails(ssn=123, dob="01/01/1970"))
house = House(name="my house", people=[person])
await house.insert(link_rule=WriteRules.WRITE)
print(f"Created {house=}")

Outputs:

TypeError: "PersonDetails" is immutable and does not support item assignment

When I first create the Person document and then try to link this to the House, it fails on the linking action;

person = Person(first_name="john", last_name="doe", details=PersonDetails(ssn=123, dob="01/01/1970"))
await person.insert()
print(f"Created {person=}")

house = House(name="my house", people=[person])
await house.insert(link_rule=WriteRules.WRITE)
print(f"Created {house=}")

It successfully performs person.insert() and then raises the error at the `house.insert():

Created person=Person(id=ObjectId('649450d942a9b7dbc587940b'), revision_id=None, first_name='john', last_name='doe', details=PersonDetails(ssn=123, dob='01/01/1970'))
Traceback (most recent call last):
... (ommitted)
  File "/usr/local/lib/python3.11/site-packages/beanie/odm/documents.py", line 607, in update
    merge_models(self, result)
  File "/usr/local/lib/python3.11/site-packages/beanie/odm/utils/parsing.py", line 24, in merge_models
    merge_models(left_value, right_value)
  File "/usr/local/lib/python3.11/site-packages/beanie/odm/utils/parsing.py", line 35, in merge_models
    left.__setattr__(k, right_value)
  File "pydantic/main.py", line 359, in pydantic.main.BaseModel.__setattr__
TypeError: "PersonDetails" is immutable and does not support item assignment

Ways to not get this error:

  • Rewrite to 2-step approach and leave out the link_rule=WriteRules.WRITE (viable workaround, but quite verbose)
  • Remove frozen = True (not a viable workaround in my case)

Expected behavior

Same as when Person would not be a Document but just a BaseModel;

class PersonDetails(BaseModel):
    ssn: int
    dob: str

    class Config:
        frozen = True

class Person(BaseModel):
    first_name: str
    last_name: str
    details: PersonDetails

class House(Document):
    name: str
    people: list[Person]


await init_beanie(database=db, document_models=[House])

person = Person(first_name="john", last_name="doe", details=PersonDetails(ssn=123, dob="01/01/1970"))
house = House(name="my house", people=[person])
await house.insert(link_rule=WriteRules.WRITE)
print(f"Created {house=}")

Which inserts without problems:

Created house=House(id=ObjectId('64944f0e5d3bc3eb3a5b495e'), revision_id=None, name='my house', people=[Person(first_name='john', last_name='doe', details=PersonDetails(ssn=123, dob='01/01/1970'))])

Additional context

n/a

@Mark90 Mark90 changed the title [BUG] Linking documents with a frozen BaseModel raises TypeError [BUG] Updating documents with a frozen BaseModel as field raises TypeError Jun 23, 2023
@Mark90
Copy link
Author

Mark90 commented Jun 23, 2023

Actually, it looks like Link is irrelevant here. I can reproduce it with this smaller example where I create and then update a document:

class PersonDetails(BaseModel):
    ssn: int
    dob: str

    class Config:
        frozen = True


class Person(Document):
    first_name: str
    last_name: str
    details: PersonDetails

    class Settings:
        use_state_management = True


await init_beanie(database=db, document_models=[Person])

person = Person(first_name="john", last_name="doe", details=PersonDetails(ssn=123, dob="01/01/1970"))
await person.insert()
print(f"Created {person=}")

person.first_name = "jane"
# await person.save()  # TypeError: "PersonDetails" is immutable and does not support item assignment
await person.save_changes()  # TypeError: "PersonDetails" is immutable and does not support item assignment
print(f"Updated {person=}")

The .insert() works, but when I update any (non-frozen) attribute, both .save() and .save_changes() raise the already mentioned TypeError

Created person=Person(id=ObjectId('6495af9d20f78b1f0b244ee7'), revision_id=None, first_name='john', last_name='doe', details=PersonDetails(ssn=123, dob='01/01/1970'))
Traceback (most recent call last):
  ... (ommitted)
  File "/usr/local/lib/python3.11/site-packages/beanie/odm/utils/parsing.py", line 24, in merge_models
    merge_models(left_value, right_value)
  File "/usr/local/lib/python3.11/site-packages/beanie/odm/utils/parsing.py", line 35, in merge_models
    left.__setattr__(k, right_value)
  File "pydantic/main.py", line 359, in pydantic.main.BaseModel.__setattr__
TypeError: "PersonDetails" is immutable and does not support item assignment

@Mark90
Copy link
Author

Mark90 commented Jun 23, 2023

In 1.17.0 this doesn't occur, it seems to have been introduced in 1.18.0

@roman-right roman-right added the bug Something isn't working label Jun 27, 2023
@roman-right
Copy link
Member

Hello,
I'll check this. Thank you for the catch

@jayrahdevore
Copy link

Just ran into this as well. Rolling back to 1.17.0 took care of the issue

@roman-right
Copy link
Member

This bug is fixed in #669. Please try

@roman-right roman-right removed the bug Something isn't working label Aug 21, 2023
@Mark90
Copy link
Author

Mark90 commented Aug 24, 2023

Thank you!

Actually it already seems to have been fixed in version 1.21.0, when I upgrade to that version all of our testcases pass (and on 1.20.0 they didn't pass yet). Awesome :)

When I pip install -U git+https://github.com/roman-right/beanie.git@fix/aug_2023 I see some new errors in our tests, I'll leave a comment on that PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants