Postgres auto incrementing ID of int type - postgresql

I am learning fastapi and I've created a model in models.py as follows:
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, nullable=False)
title = Column(String, nullable=False)
content = Column(String, nullable=False)
published = Column(Boolean, server_default='TRUE')
created_at = Column(TIMESTAMP(timezone=True), nullable=False, server_default=text('now()'))
I have mentioned id as int but it is auto incrementing like serial and also serial is automatically set to default. Even if I am passing id from postman it is still getting auto incremented and discarding my sent value.

You have to explicitly disable the autoincrement functionality - an integer primary key without a default value will usually have autoincrement functionality added automagically:
The default value is the string "auto", which indicates that a single-column (i.e. non-composite) primary key that is of an INTEGER type with no other client-side or server-side default constructs indicated should receive auto increment semantics automatically.
Instead, set the autoincrement argument explicitly to False:
False (this column should never have auto-increment semantics)

Related

Primary key generator for pydantic model with FastAPI?

I am trying to create objects with a primary integer key, but when using fastAPI, I cannot get the keys to autogenerate. The validator says an ID needs to be passed in to pass validation, which makes sense, but I want to instead have an autokey each time a new object is created. How does one do this?
class PinSchema(BaseModel): id: Optional[int] = Field(primary_key=True)
class PinSchema(BaseModel):
id: Optional[int] = Field(primary_key=True)
//some other fields here
class Config:
allow_population_by_field_name = True
arbitrary_types_allowed = True
This is my current model for the object. I set the int to optional, but now it just has the default as null, not the autogen int.
I tried to set the field as optional, but it just defaults to null even after I set the default to a primary key field. I also tried just leaving it as int = Field(primary_key=True), but no luck, it fails the validation. Is there a way to exclude the id field from the validation process with fastAPI?

How to query SQLAlchemy/PostgreSQL table for existence of an object where PK is UUID

Apologies if this is poorly phrased as I'm pretty new to SQLAlchemy. Suppose I have the following setup:
class Student(Base):
__tablename__ = 'student'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(50), nullable=False)
...and I create a new Student and add it to the table:
new_student = Student(name="Nick")
session.add(new_student)
session.commit()
Assuming that primary key is solely the UUID, how can I check the table for the existence of the specific object referenced by new_student? Is it possible to do something like:
student = session.query(Student).filter(new_student)
Everything I've seen in my searches requires you to use a table column, but the only guaranteed unique column in my table is the UUID, which I don't know until after it's created.
Thanks for the help!

How to Initialise & Populate a Postgres Database with Circular ForeignKeys in SQLModel?

Goal:
I'm trying to use SQLModel (a wrapper that ties together pydantic and sqlalchemy) to define and interact with the back-end database for a cleaning company. Specifically, trying to model a system where customers can have multiple properties that need to be cleaned and each customer has a single lead person who has a single mailing property (to contact them at). Ideally, I want to be able to use a single table for the mailing properties and cleaning properties (as in most instances they will be the same).
Constraints:
Customers can be either individual people or organisations
A lead person must be identifiable for each customer
Each person must be matched to a property (so that their mailing address can be identified)
A single customer can have multiple properties attached to them (e.g. for a landlord that includes cleaning as part of the rent)
The issue is that the foreign keys have a circular dependency.
Customer -> Person based on the lead_person_id
Person -> Property based on the mailing_property_id
Property -> Customer based on the occupant_customer_id
Code to reproduce the issue:
# Imports
from typing import Optional, List
from sqlmodel import Session, Field, SQLModel, Relationship, create_engine
import uuid as uuid_pkg
# Defining schemas
class Person(SQLModel, table=True):
person_id: uuid_pkg.UUID = Field(default_factory=uuid_pkg.uuid4, primary_key=True, index=True, nullable=True)
first_names: str
last_name: str
mailing_property_id: uuid_pkg.UUID = Field(foreign_key='property.property_id')
customer: Optional['Customer'] = Relationship(back_populates='lead_person')
mailing_property: Optional['Property'] = Relationship(back_populates='person')
class Customer(SQLModel, table=True):
customer_id: uuid_pkg.UUID = Field(default_factory=uuid_pkg.uuid4, primary_key=True, index=True, nullable=True)
lead_person_id: uuid_pkg.UUID = Field(foreign_key='person.person_id')
contract_type: str
lead_person: Optional['Person'] = Relationship(back_populates='customer')
contracted_properties: Optional[List['Property']] = Relationship(back_populates='occupant_customer')
class Property(SQLModel, table=True):
property_id: uuid_pkg.UUID = Field(default_factory=uuid_pkg.uuid4, primary_key=True, index=True, nullable=True)
occupant_customer_id: uuid_pkg.UUID = Field(foreign_key='customer.customer_id')
address: str
person: Optional['Person'] = Relationship(back_populates='mailing_property')
occupant_customer: Optional['Customer'] = Relationship(back_populates='contracted_properties')
# Initialising the database
engine = create_engine(f'postgresql://{DB_USERNAME}:{DB_PASSWORD}#{DB_URL}:{DB_PORT}/{DB_NAME}')
SQLModel.metadata.create_all(engine)
# Defining the database entries
john = Person(
person_id = 'eb7a0f5d-e09b-4b36-8e15-e9541ea7bd6e',
first_names = 'John',
last_name = 'Smith',
mailing_property_id = '4d6aed8d-d1a2-4152-ae4b-662baddcbef4'
)
johns_lettings = Customer(
customer_id = 'cb58199b-d7cf-4d94-a4ba-e7bb32f1cda4',
lead_person_id = 'eb7a0f5d-e09b-4b36-8e15-e9541ea7bd6e',
contract_type = 'Landlord Premium'
)
johns_property_1 = Property(
property_id = '4d6aed8d-d1a2-4152-ae4b-662baddcbef4',
occupant_customer_id = 'cb58199b-d7cf-4d94-a4ba-e7bb32f1cda4',
address = '123 High Street'
)
johns_property_2 = Property(
property_id = '2ac15ac9-9ab3-4a7c-80ad-961dd565ab0a',
occupant_customer_id = 'cb58199b-d7cf-4d94-a4ba-e7bb32f1cda4',
address = '456 High Street'
)
# Committing the database entries
with Session(engine) as session:
session.add(john)
session.add(johns_lettings)
session.add(johns_property_1)
session.add(johns_property_2)
session.commit()
Results in:
ForeignKeyViolation: insert or update on table "customer" violates foreign key constraint "customer_lead_person_id_fkey"
DETAIL: Key (lead_person_id)=(eb7a0f5d-e09b-4b36-8e15-e9541ea7bd6e) is not present in table "person".
This issue is specific to Postgres, which unlike SQLite (used in the docs) imposes constraints on foreign keys when data is being added. I.e. replacing engine = create_engine(f'postgresql://{DB_USERNAME}:{DB_PASSWORD}#{DB_URL}:{DB_PORT}/{DB_NAME}') with engine = create_engine('sqlite:///test.db') will let the database be initialised without causing an error - however my use-case is with a Postgres DB.
Attempted Solutions:
Used link tables between customers/people and properties/customers - no luck
Used Session.exec with this code from SO to temporarily remove foreign key constraints then add them back on - no luck
Used primary joins instead of foreign keys as described in this SQLModel Issue - no luck

JPA generate partial unique key

I have a table with 3 columns
UUID - A UUID that is the primary key of the table
ID - A human readable ID of the resource (for a new resource, the ID should be automatically generated by a sequence)
Version - A version number
I am using JPA.
The table can contain multiple records with the same "human readable" ID and different versions.
I would like to be able to insert a new record without specifying the ID: the database should generate the ID automatically.
At the same time, when I need to insert a new version of the same resource, I would like to be able to insert a new row specifying the ID.
I have created a table where the UUID is the primary key, ID is defined as "integer generated by default as identity" and version is just an integer.
Using SQL query I can do what I want, but I do not know how to do it using JPA.
If I define the column as:
#Column(name="ID", insertable = false, updatable = false, nullable = false)
I can insert new records but the ID is always generated as new even if the resource already has one because the insert does not include the column.
If I define the column as:
#Column(name="ID", insertable = true, updatable = false, nullable = false)
The insert include the column and I am able to insert new rows specifying the ID but I cannot insert a row without the ID because the SQL generated is passing a null value for that column.
UPDATE
I have modified the configuration adding the annotation #Generated:
#Generated(value = GenerationTime.INSERT)
#Column(name="ID", updatable = false, nullable = false)
private Integer id;
With this, I am having the same problem: if I pass a value for id, the database is still generating a new one.
You can try to use #DynamicInsert annotation.
Assuming that you have the following table:
create table TST_MY_DATA
(
dt_id uuid,
dt_auto_id integer generated by default as identity,
dt_version integer,
primary key(dt_id)
);
Appropriate entity will look like this:
#Entity
#Table(name = "TST_MY_DATA")
#DynamicInsert
public class TestData
{
#Id
#GeneratedValue
#Column(name = "dt_id")
private UUID id;
// Unfortunately you cannot use #Generated annotation here,
// otherwise this column will be always absent in hibernate generated insert query
// #Generated(value = GenerationTime.INSERT)
#Column(name = "dt_auto_id")
private Long humanReadableId;
#Version
#Column(name = "dt_version")
private Long version;
// getters/setters
}
and then you can persist entities:
TestData test1 = new TestData();
session.persist(test1);
TestData test2 = new TestData();
test2.setHumanReadableId(27L);
session.persist(test2);
session.flush();
// here test1.getHumanReadableId() is null
/*
* You can use session.refresh(entity) only after session.flush() otherwise you will have:
* org.hibernate.UnresolvableObjectException: No row with the given identifier exists:
* [this instance does not yet exist as a row in the database#ff09c202-cd17-4d4a-baea-057e475fabb9]
**/
session.refresh(test1);
// here you can use the test1.getHumanReadableId() value fetched from DB

Foreign Key could not be found with SqlAlchemy and PostgreSQL

I just started a Flask - SqlAlchemy project and am having some trouble with Foreign Keys.
I have the tables User and Portfolio. Portfolio has a foreign key to user, using username. I set up my model like this.
class User(db.Model):
__tablename__ = 'portfolio_users'
__table_args__ = {"schema":"keldan"}
username = Column(String(), primary_key=True)
date_added = Column(DateTime())
class Portfolio(db.Model):
__tablename__ = 'portfolios'
__table_args__ = {"schema":"keldan"}
id = Column('pid', Integer(), Sequence('portfolios_pid_seq'), primary_key=True)
date_added = Column(DateTime())
name = Column(String())
username = Column(String(), ForeignKey('portfolio_users.username'))
user = relationship('User', backref=backref('portfolios', cascade='save-update, merge, delete, delete-orphan'))
The error I get when I try to run a simple select all query is:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'portfolios.username' could not find table 'portfolio_users' with which to generate a foreign key to target column 'username'
The tables are created like this:
CREATE TABLE keldan.portfolio_users
(
username text NOT NULL,
date_added date NOT NULL,
CONSTRAINT users_pk PRIMARY KEY (username)
)
WITH (
OIDS=FALSE
);
CREATE TABLE keldan.portfolios
(
pid serial NOT NULL,
username text NOT NULL,
date_added date NOT NULL,
name text NOT NULL,
CONSTRAINT portfolios_pk PRIMARY KEY (pid),
CONSTRAINT portfolios_fk FOREIGN KEY (username)
REFERENCES keldan.portfolio_users (username) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
I have spent the better part of a day trying to figure this out or making workarounds using primaryjoin but nothing seems to work.
I finally found the answer I was looking for here
If you are not using the default schema (public) then it's not enough to specify the schema for each class, but I need to specify it in the foreign key as well.
username = Column(String(), ForeignKey('keldan.portfolio_users.username'))