sqlmodel icon indicating copy to clipboard operation
sqlmodel copied to clipboard

Flowing large hash values to Postgres BigInt

Open cworkschris opened this issue 4 years ago • 10 comments

First Check

  • [X] I added a very descriptive title to this issue.
  • [X] I used the GitHub search to find a similar issue and didn't find it.
  • [X] I searched the SQLModel documentation, with the integrated search.
  • [X] I already searched in Google "How to X in SQLModel" and didn't find any information.
  • [X] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [X] I already checked if it is not related to SQLModel but to Pydantic.
  • [X] I already checked if it is not related to SQLModel but to SQLAlchemy.

Commit to Help

  • [X] I commit to help with one of those options 👆

Example Code

from sqlalchemy import BigInteger
from typing import Optional


from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    #case A
    normhash: Optional[int] = Field(default=None, index=True)       
    #case B
    #normhash: Optional[BigInteger] = Field(default=None, index=True)           


hero_1 = Hero(normhash=1559512409891417611)

engine = create_engine("postgresql://app:newbpw@somehost:5400/some_db)


SQLModel.metadata.create_all(engine)

with Session(engine) as session:
    session.add(hero_1)
    session.commit()
    # in case A: DataError: (psycopg2.errors.NumericValueOutOfRange) integer out of range
    # (case B the code won't even finish coming up - no validator error)
    session.refresh(hero_1)
    print(hero_1)

Description

Using your default Hero example. Replace the fields with a hash field. Using postgres, I'm unable to set up the field for big integers. Case A: using standard int results in NumericValueOutOfRange at the psycopg2 level.

So, case B: trying to force a postgres BIGINT, using sqlalchemy BigInteger, I get: File "pydantic/validators.py", line 715, in find_validators RuntimeError: no validator found for <class 'sqlalchemy.sql.sqltypes.BigInteger'>, see arbitrary_types_allowed in Config

I know it involves all the different levels, but it seems like a model of use problem (and I had validator problems before that ended up being a change in the way I use sqlmodel.)

Thanks for your creation of sqlmodel - so far I've really enjoyed it along with fastapi!

Operating System

Linux

Operating System Details

No response

SQLModel Version

0.0.4

Python Version

3.8.12

Additional Context

No response

cworkschris avatar Dec 14 '21 11:12 cworkschris

class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    normhash: Optional[int] = Field(default=None, index=True, sa_column=Column(BigInteger())) 

yinziyan1206 avatar Dec 28 '21 02:12 yinziyan1206

@yinziyan1206 This works if the field is not the primary key. If I want to create a primary key of type BigInteger, I get the exception:

sqlalchemy.exc.ArgumentError: Mapper mapped class Hero->hero could not assemble any primary key columns for mapped table 'hero'

e.g. with id: int = Field(default=None, index=True, sa_column=Column(BigInteger())) Any idea how to solve this?

unidesigner avatar Jan 07 '22 14:01 unidesigner

@unidesigner uhhhh, you can make it a column in sqlalchemy just like this: id: int = Field( default_factory=next_val, sa_column=Column(BigInteger(), primary_key=True, autoincrement=False) ) and also, you can use sa_column_kwargs to set a column in sqlalchemy. i think it is flexible

yinziyan1206 avatar Jan 10 '22 02:01 yinziyan1206

thanks @yinziyan1206 - this did the trick!

unidesigner avatar Jan 11 '22 16:01 unidesigner

If you want to reference an id field created in this manner as a foreign key, make sure to do it like this:

customer_id: int = Field(None, foreign_key='customer.id', sa_column=Column(BigInteger()))

tobiasfeil avatar Jan 02 '23 13:01 tobiasfeil

If you want to reference an id field created in this manner as a foreign key, make sure to do it like this:

customer_id: int = Field(None, foreign_key='customer.id', sa_column=Column(BigInteger()))

This doesn't create foreign key constrant because if sa_column is not undefined it is returned before any parameters are parsed. And customer_id: int = Field(None, sa_column=Column(BigInteger(), foreign_key='customer.id')) the same behavor...

yudytskiy avatar Aug 21 '23 13:08 yudytskiy

To make it work you should do: customer_id: int = Field(sa_column=Column(BigInteger(), ForeignKey('customer.id')))

yudytskiy avatar Aug 22 '23 13:08 yudytskiy

Just FYI, to get the bigserial column like I wanted, I had to modify @yinziyan1206's solution a bit:

from typing import Optional
from sqlalchemy import Column, BigInteger
from sqlmodel import Field, SQLModel

class Users(SQLModel, table=True):
    id: Optional[int] = Field(default=None, sa_column=Column(BigInteger(), primary_key=True, autoincrement=True))
    name: str

This generates the following in postgres:

CREATE TABLE public."users" (
	id bigserial NOT NULL,
	name varchar NOT NULL,
        PRIMARY KEY (id)
);

mrexodia avatar Aug 30 '23 13:08 mrexodia