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

masoniteorm.query.QueryBuilder.QueryBuilder.create() #843

Closed
moluwole opened this issue Feb 20, 2023 · 12 comments
Closed

masoniteorm.query.QueryBuilder.QueryBuilder.create() #843

moluwole opened this issue Feb 20, 2023 · 12 comments
Labels
bug An existing feature is not working as intended

Comments

@moluwole
Copy link

Hello guys, I have this blogpost https://testdriven.io/blog/masonite-orm-fastapi/.

I tried upgrading the dependencies of the post so as to have the latest versions but when I try to save to the database, I get this error

"masoniteorm.query.QueryBuilder.QueryBuilder.create() got multiple values for keyword argument 'id_key'"

The whole project works fine with the 2.18.6 version but anything higher than that results in the error above

@josephmancuso
Copy link
Member

Can you put the whole code snippet here for me please? and also possibly any stack traces

@josephmancuso josephmancuso added the bug An existing feature is not working as intended label Feb 20, 2023
@moluwole
Copy link
Author

StackTrace

Traceback (most recent call last):
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 407, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/fastapi/applications.py", line 270, in __call__
    await super().__call__(scope, receive, send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/routing.py", line 706, in __call__
    await route.handle(scope, receive, send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/fastapi/routing.py", line 237, in app
    raw_response = await run_endpoint_function(
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/fastapi/routing.py", line 165, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/starlette/concurrency.py", line 41, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/anyio/to_thread.py", line 31, in run_sync
    return await get_asynclib().run_sync_in_worker_thread(
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 937, in run_sync_in_worker_thread
    return await future
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 867, in run
    result = context.run(func, *args)
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/./main.py", line 36, in add_user
    user.save() # saves user details to the database.
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/masoniteorm/models/Model.py", line 870, in save
    result = self.create(
  File "/home/moluwole/Documents/code/writing/fastapi-masonite/.env/lib/python3.10/site-packages/masoniteorm/models/Model.py", line 565, in create
    return cls.builder.create(
TypeError: masoniteorm.query.QueryBuilder.QueryBuilder.create() got multiple values for keyword argument 'id_key'

@moluwole
Copy link
Author

@app.post("/api/v1/users", response_model=schema.UserResult)
def add_user(user_data: schema.UserCreate):
    user = User.where("email", user_data.email).get()
    if user:
        raise HTTPException(status_code=400, detail="User already exists")
    user = User()
    user.email = user_data.email
    user.name = user_data.name
    user.address = user_data.address
    user.sex = user_data.sex
    user.phone_number = user_data.phone_number

    user.save() # saves user details to the database.
    return user

@moluwole
Copy link
Author

@josephmancuso do note that this error does not occur on the 2.18.6 version of masonite-orm

@moluwole
Copy link
Author

My models/User.py file

""" User Model """

from masoniteorm.models import Model
from masoniteorm.relationships import has_many


class User(Model):
   """User Model"""

   @has_many("id", "user_id")
   def posts(self):
       from .Post import Post

       return Post

   @has_many("id", "user_id")
   def comments(self):
       from .Comment import Comment

       return Comment

@moluwole
Copy link
Author

moluwole commented Feb 21, 2023

My Migration

"""MigrationForUserTable Migration."""

from masoniteorm.migrations import Migration


class MigrationForUserTable(Migration):
    def up(self):
        """
        Run the migrations.
        """
        with self.schema.create("users") as table:
            table.increments("id")
            table.string("name")
            table.string("email").unique()
            table.text("address").nullable()
            table.string("phone_number", 11).nullable()
            table.enum("sex", ["male", "female"]).nullable()
            table.timestamps()

    def down(self):
        """
        Revert the migrations.
        """
        self.schema.drop("Users")

@josephmancuso
Copy link
Member

Have you tried on 2.19.1?

@moluwole
Copy link
Author

Yup...that's the version giving me this error

@josephmancuso
Copy link
Member

Thanks that actually gives me an idea of where the issue is. I'll take a look as soon as i can

@langsamer
Copy link

I just ran into the same bug.
in models/Model.py, line 869, self.create() is called with keyword argument id_key=self.get_primary_key(), however the create method does not expect id_key as an argument, so it gets passed inside **kwargs.
Within the method create() (line 565) cls.builder.create() is called with id_key=cls.__primary_key__ and **kwargs, so id_key now is passed twice.

@tpow
Copy link

tpow commented Mar 22, 2023

Yes, I hit this as well. In my case I have a classmethod in my model that is essentially working like an alternative constructor. It does something like this:

from masoniteorm.models import Model
                                                                           
class MyClass(Model): 
    @classmethod                                                                 
    def create(cls, variable):
        c = cls()
        c.field = variable
        c.otherfield = 'something else'
        c.save()  # <-- fails with TypeError multiple values for id_key
        return c.id

The bug is caused by the Model.save adding the id_key when it calls create. This means Model.create gets the id_key in the kwargs, but then adds it again. Arguably, save doesn't need to add it.

I worked around this by overriding the create function in my model. I simply lifted the create from Model and then changed it like below.

    @classmethod                                                                 
    def create(                                                                  
        cls,                                                                     
        dictionary: Dict[str, Any] = None,                                       
        query: bool = False,                                                     
        cast: bool = False,                                                      
        **kwargs,                                                                
    ):                                                                           
        """Creates new records based off of a dictionary as well as data set on the model
        such as fillable values.                                                 
                                                                                 
        Args:                                                                    
            dictionary (dict, optional): [description]. Defaults to {}.          
            query (bool, optional): [description]. Defaults to False.            
            cast (bool, optional): [description]. Whether or not to cast passed values.
                                                                                 
        Returns:                                                                 
            self: A hydrated version of a model                                  
        """                                                                      
        if query:                                                                
            return cls.builder.create(                                           
                dictionary, query=True, id_key=cls.__primary_key__, cast=cast, **kwargs
            ).to_sql()                                                           
                                                                                 
        return cls.builder.create(                                               
            # dictionary, id_key=cls.__primary_key__, cast=cast, **kwargs  # <-- was this
            dictionary, cast=cast, **kwargs  # <-- It should be this       
        )  

Again, looking at the code history, it looks the real problem is that save is adding the id_key, but changing create like above would also fix it.

@josephmancuso It'd be nice if you could take a look at this and get a fix released. Thanks! Tim

This was referenced Apr 10, 2023
@josephmancuso
Copy link
Member

this is fixed in v2.19.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An existing feature is not working as intended
Projects
None yet
Development

No branches or pull requests

4 participants