User with role Product not in ['Customer', 'Admin'] in fastApi - python

I getting error related the role & I don't know What I mistake.
enter image description here
enter image description here
Database Models:
class Role(Base):
__tablename__ = "role"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), index=True,default="Product")
role_onwer = relationship("User",back_populates="role",uselist=False)
role_onwer1 = relationship("Customer",back_populates="role1",uselist=False)
def __repr__(self):
return f'<Role {self.name}>'
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50))
email = Column(String(255), index=True,unique=True)
password = Column(String(80))
created_at = Column(DateTime, default=datetime.datetime.utcnow)
updated_at = Column(
DateTime,
default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow,
)
last_login = Column(DateTime, nullable=True)
user = relationship("Customer",back_populates="user_onwer",uselist=False)
restaurant = relationship("Restaurant",back_populates="restaurant_onwer",uselist=False)
role_id = Column(Integer, ForeignKey('role.id'))
role = relationship("Role",back_populates="role_onwer",uselist=False)
def __repr__(self):
return f'<User {self.email}>'
class Customer(Base):
__tablename__ = "customer"
id = Column(Integer, primary_key=True)
name = Column(String(255))
phone = Column(String(255))
address = Column(String(255))
city = Column(String(255))
zipCode = Column(String(255))
created_at = Column(DateTime, default=datetime.datetime.utcnow)
updated_at = Column(
DateTime,
default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow,
)
user_id = Column(Integer, ForeignKey("user.id"))
role_id = Column(Integer, ForeignKey('role.id'))
role1 = relationship("Role",back_populates="role_onwer1",uselist=False)
user_onwer = relationship("User",back_populates="user",uselist=False)
orders = relationship("Order", back_populates="order_onwer",uselist=False)
def __repr__(self):
return f'<Customer {self.name}>'
pydantic Models
T = TypeVar('T')
class Response(GenericModel, Generic[T]):
code: str
status: str
message: str
result: Optional[T]
# Customer Request Schemas
class CustomerSchema(BaseModel):
username: str
email: EmailStr
password: str
name: str
phone: str
address: str
city: str
zipCode: str
Crud funtions: customerController
def get_customer_by_id(db: Session, customer_id: int):
return db.query(Customer).filter(Customer.id == customer_id).first()
# This one i have implemented for you
def create_customer(db: Session, customer: CustomerSchema):
new_role = Role()
db.add(new_role)
db.commit()
db.refresh(new_role)
role_ids = new_role.id
new_user = User(username=customer.username, email=customer.email, password=Hash.bcrypt(customer.password),role_id =role_ids)
# new_user = User(username=customer.username, email=customer.email, password=Hash.bcrypt(customer.password),
# role_id=2)
db.add(new_user)
db.commit()
db.refresh(new_user)
user_ids = new_user.id
print(user_ids)
_customer = Customer(name=customer.name, phone=customer.phone, address=customer.address,
city=customer.city, zipCode=customer.zipCode,user_id=user_ids,role_id =role_ids)
db.add(_customer)
db.commit()
db.refresh(_customer)
return _customer
def remove_customer(db: Session, customer_id: int):
_customer = get_customer_by_id(db=db, customer_id=customer_id)
db.delete(_customer)
db.commit()
def update_customer(db: Session, customer_id: int, name: str, email: str, phone: int, customerID: str, address: str,
city: str, zipCode: str):
_customer = get_customer_by_id(db=db, customer_id=customer_id)
_customer.name = name
_customer.email = email
_customer.phone = phone
_customer.customerID = customerID
_customer.address = address
_customer.city = city
_customer.zipCode = zipCode
db.commit()
db.refresh(_customer)
return _customer
customerRouter:
#router.get('/')
async def get(db: Session = Depends(get_db)):
_customer = customerController.get_customer(db, 0, 100)
return Response(code=200, status='OK', message='Success fetch all data', result=_customer).dict(exclude_none=True)
AuthController
class RoleChecker:
def __init__(self, allowed_roles: List):
self.allowed_roles = allowed_roles
def __call__(self, user: schemas.User = Depends(get_current_user)):
if user.role.name not in self.allowed_roles:
print(f"User with role {user.role.name} not in {self.allowed_roles}")
raise HTTPException(status_code=403, detail="Operation not permitted")
return user
customer_access = RoleChecker(['Customer', 'Admin'])
Thank you
I need to have so user cannot retrieve data without being logged in.
and a customer is a user who buys stuff and they may only have the following access:
/customer BY ID:
Read (only they own information)
Update:(only your own data)

Related

How to get sqlalchemy relationship list in pydantic model?

when I try to get organization by id (inn) in devices_list field is null (device is exist):
{
inn: 8481406044,
organization_name: "slava bebrow",
devices_list: null
}
models.py:
class Organization(Base):
__tablename__ = "organizations_table"
inn = Column(BigInteger, primary_key=True, index=False)
organization_name = Column(String, nullable=False, unique=True)
devices = relationship("Device", backref="organizations_table")
class Device(Base):
__tablename__ = "devices_table"
uuid = Column(String, primary_key=True, index=False)
device_name = Column(String, nullable=False, unique=True)
organization_id = Column(BigInteger, ForeignKey("organizations_table.inn"), nullable=True)
pydantic_models.py:
class OrganizationBase(BaseModel):
inn: int
organization_name: str
class Config:
orm_mode = True
class Organization(OrganizationBase):
devices_list: list['DeviceBase'] = None
class DeviceBase(BaseModel):
uuid: str
device_name: str
organization_id: int | None
class Config:
orm_mode = True
functions to get organization:
def get_organization(db: Session, organization_id: int):
db_organization = db.query(models.Organization).filter(models.Organization.inn == organization_id).first()
if db_organization is None:
raise HTTPException(status_code=404, detail="Organization not found")
return db_organization
when i try to print(db_organization.devices[0].uuid) i get a list with objects
#app.get("/organizations/{organization_id}", response_model=pydantic_models.Organization)
def get_organization(organization_id, db: Session = Depends(get_db)):
return al.get_organization(db=db, organization_id=organization_id)
I think problem is in pydantic model, but i don't know how to fix it.
I expect a list of devices in field, not null

Handling nested schema in fast api while storing data

I have a nested schema in fast api below is the book model`
class Book(Base):
id = Column(Integer, primary_key=True, index=True)
title = Column(String(256), index=True)
subtitle = Column(String(256), index=True)
narrator = Column(String(128), index=True)
publisher_id = Column(Integer, ForeignKey("publisher.id"))
publishers = relationship("Publisher", back_populates="books")
length = Column(Integer, index=True) # length in minutes
release_date = Column(Date, index=True)
language = Column(String(32), index=True)
rating = Column(Float, index=True)
description = Column(ARRAY(String(1024)), index=True)
cover_image = Column(String(256), index=True)
owner_id = Column(Integer, ForeignKey("user.id"))
owner = relationship("User", back_populates="books")
That has a related to publisher model as below `
class Publisher(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), index=True, nullable=False)
books = relationship("Book", back_populates="publishers")
and their respective schema as below
class BookBase(BaseModel):
title: Optional[str] = Field(..., title="Book title", example="Atomic Habits")
subtitle: Optional[str] = Field(
None,
title="Book subtitle",
example="An Easy & Proven Way to Build Good Habits & Break Bad Ones",
)
narrator: Optional[str] = Field(..., title="Narrator", example="James Clear")
# publisher_id: Optional[int] = Field(None, title="Publisher ID")
publisher: Optional[PublisherBase] = Field(None, title="Publisher")
length: Optional[int] = Field(..., title="Length", example=335)
release_date: Optional[datetime.date] = Field(..., title="Release Date")
language: Optional[str] = Field(..., title="Language", example="English")
rating: Optional[float] = Field(..., title="Rating", example=4.8)
description: Optional[list] = Field(..., title="Description")
cover_image: Optional[HttpUrl] = Field(
...,
title="Book Cover Image",
example="https://m.media-amazon.com/images/I/513Y5o-DYtL._SL500_.jpg",
)
class Config:
orm_mode = True
and publisher schema
class PublisherBase(BaseModel):
name: Optional[str] = Field(..., title="Publisher name", example="Penguin Audio")
class Config:
orm_mode = True
based on the relationship above i have the below crud method that accepts book item with a nested publisher `
class CRUDBook(CRUDBase[Book, BookCreate, BookUpdate]):
def create_with_owner(
self, db: Session, *, obj_in: BookCreate, owner_id: int
) -> Book:
# check if the publisher exists
publisher = crud.publisher.show(db=db, name=obj_in.publisher.name)
if not publisher:
# create a new publisher
publisher = crud.publisher.create(
db=db, obj_in=PublisherCreate(name=obj_in.publisher.name)
)
obj_in_data = jsonable_encoder(obj_in_data)
db_obj = self.model(
**obj_in_data,
owner_id=owner_id,
publisher_id=publisher.id,
)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
That is consumed with the below API method
#router.post("/", response_model=schemas.Book)
def create_book(
*,
db: Session = Depends(deps.get_db),
book_in: schemas.BookCreate,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Create new book.
"""
book = crud.book.create_with_owner(db=db, obj_in=book_in, owner_id=current_user.id)
return book
with the above, I have my schema as how I wanted that is
Example Value
Schema
{
"title": "Atomic Habits",
"subtitle": "An Easy & Proven Way to Build Good Habits & Break Bad Ones",
"narrator": "James Clear",
"publisher": {
"name": "Penguin Audio"
},
"length": 335,
"release_date": "2022-12-29",
"language": "English",
"rating": 4.8,
"description": [
"string"
],
"cover_image": "https://m.media-amazon.com/images/I/513Y5o-DYtL._SL500_.jpg"
}
But the issue comes when passing data for storage. I face a TypeError TypeError: 'publisher' is an invalid keyword argument for Book
I think that the type error might be due to the nested publisher object that I am accepting in my schema and possibly passing to the book.
Is there a better way to handle this scenario?
You accidentally named the parameter publishers (plural) in your Book model. Change it to publisher (and also the back_populates in Publisher.books), and you should be fine.

Trying to add a record to tables with many-to-many relationship using POST - FastAPI + SQLalchemy

I have some pydantic and SQLalchemy models
game_users = Table('game_users', Base.metadata,
Column('game_id', ForeignKey('games.id'), primary_key=True),
Column('user_id', ForeignKey('users.id'), primary_key=True)
)
class Game(Base):
__tablename__ = 'games'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
users = relationship("User", secondary="game_users", back_populates='games')
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
age = Column(Integer)
email = Column(String, nullable=False, unique=True)
games = relationship("Game", secondary="game_users", back_populates='users')
class UserBase(BaseModel):
id: int
name: str
age: int = Query(ge=0, le=100)
email: str
class Config:
orm_mode = True
class GameBase(BaseModel):
id: int
name: str
class Config:
orm_mode = True
class UsersOut(UserBase):
games: List[GameBase]
class GamesOut(GameBase):
users: List[UserBase]
And I need to add entries by POST method, but I don't know how to do it exactly. I tried something like this:
#app.post('/connect/{uid}/{gid}')
def connect_to_game(uid: int, gid: int, db: Session = Depends(get_db)):
game = db.query(Game).filter(Game.id == gid).first()
user = db.query(User).filter(User.id == uid).first()
user_games = user(games=[game.id])
game_users = game(users=[user.id])
db.add_all([user_games, game_users])
db.commit()
return f'{game.name} successfully connected to {user.name}'
But it, certainly, doesn't work.
I tried to find information in pydantic and FastAPI documentations, but I couldn't. So I'll be really appreciated for any help or ideas.
I define models as:
game_users = Table('game_users', DBBase.metadata,
Column('game_id', ForeignKey('games.id'), primary_key=True),
Column('user_id', ForeignKey('users.id'), primary_key=True)
)
class Game(DBBase):
__tablename__ = 'games'
id = Column(Integer, primary_key=True)
name = Column(String(10), nullable=False)
users = relationship("User", secondary=game_users, backref='games')
class User(DBBase):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(10), nullable=False)
simplize back_populates by backref.
then assosicate objects:
>>> g1 = Game(name='g1')
>>> g2 = Game(name='g2')
>>> u1 = User(name='u1')
>>> u2 = User(name='u2')
>>> g1.users.extend([u1,u2])
>>> g2.users.extend([u1,u2])
>>> g1.users
[<models.User object at 0x105166b50>, <models.User object at 0x10519cfa0>]
>>> u1.games
[<models.Game object at 0x10512a280>, <models.Game object at 0x105166130>]
>>> s.add_all([g1,g2,u1,u2]) # s is db session
>>> s.commit()
So you just need to use the same type of adding relationships as above in your post method:
#app.post('/connect/{uid}/{gid}')
def connect_to_game(uid: int, gid: int, db: Session = Depends(get_db)):
game = db.query(Game).filter(Game.id == gid).first()
user = db.query(User).filter(User.id == uid).first()
game.users.append(user)
# no new instance, no need `add_all`
# db.add_all([user_games, game_users])
db.commit()
return f'{game.name} successfully connected to {user.name}'
Reference sqlalchemy many to many

One to one relation sqlalchemy

I am creating a TODO list. Each user has to have only one TODO list. ( I assume one to one model ).
If I set default value of todolist is None for each user, then create a todolist - it won't link it to the User. User.todolist will still be null. What should I do to have only one instance of todolist for each user?
My code:
class ToDoListBase(BaseModel):
items: List[ToDoItemCreate] = []
owner_id: int
class Config:
orm_mode = True
class UserBase(BaseModel):
todo: Optional[ToDoListCreate]
class Config:
orm_mode = True
Sqlalchemy models:
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
todolist = relationship('ToDoList', back_populates='owner', uselist=False)
class ToDoList(Base):
__tablename__ = 'todolists'
id = Column(Integer, primary_key=True, index=True)
owner_id = Column(Integer, ForeignKey('users.id'))
owner = relationship('User', back_populates='todolist')
What I expect to get is User where User.todolist = {items: [], owner_id: users.id}
what I get is User.todolist = null
and this is how I create todolist:
def get_or_create(self, owner_id: int, db: Session):
todolist = db.query(ToDoList).filter(ToDoList.owner_id==owner_id).first()
if not todolist:
todolist = ToDoList(
owner_id=owner_id,
)
db.add(todolist)
db.commit()
db.refresh(todolist)
return todolist

Sqlalchemy: Numeric Value out of range

When i try to add user with BIG INT, psycopg2 throws Numberic Value out of range error.
but still BIGINT is my data type in Column of my table and in local postgresql database.
Edit1: I have added the source code for chat_members model.
My table Schema code
class Users(BASE):
__tablename__ = "users"
user_id = Column(BigInteger, primary_key=True)
username = Column(UnicodeText)
def __init__(self, user_id, username=None):
self.user_id = user_id
self.username = username
def __repr__(self):
return "<User {} ({})>".format(self.username, self.user_id)
class Chats(BASE):
__tablename__ = "chats"
chat_id = Column(String(14), primary_key=True)
chat_name = Column(UnicodeText, nullable=False)
def __init__(self, chat_id, chat_name):
self.chat_id = str(chat_id)
self.chat_name = chat_name
def __repr__(self):
return "<Chat {} ({})>".format(self.chat_name, self.chat_id)
class ChatMembers(BASE):
__tablename__ = "chat_members"
priv_chat_id = Column(Integer, primary_key=True)
# NOTE: Use dual primary key instead of private primary key?
chat = Column(
String(14),
ForeignKey("chats.chat_id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False,
)
user = Column(
BigInteger,
ForeignKey("users.user_id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False,
)
__table_args__ = (UniqueConstraint("chat", "user", name="_chat_members_uc"),)
def __init__(self, chat, user):
self.chat = chat
self.user = user
def __repr__(self):
return "<Chat user {} ({}) in chat {} ({})>".format(
self.user.username,
self.user.user_id,
self.chat.chat_name,
self.chat.chat_id,
)
Users.__table__.create(checkfirst=True)
Chats.__table__.create(checkfirst=True)
ChatMembers.__table__.create(checkfirst=True)
code which throws error:
def update_user(user_id, username, chat_id=None, chat_name=None):
with INSERTION_LOCK:
user = SESSION.query(Users).get(user_id)
if not user:
user = Users(user_id, username)
SESSION.add(user)
SESSION.flush()
else:
user.username = username
if not chat_id or not chat_name:
SESSION.commit()
return
chat = SESSION.query(Chats).get(str(chat_id))
if not chat:
chat = Chats(str(chat_id), chat_name)
SESSION.add(chat)
SESSION.flush()
else:
chat.chat_name = chat_name
member = (
SESSION.query(ChatMembers)
.filter(ChatMembers.chat == chat.chat_id, ChatMembers.user == user.user_id)
.first()
)
if not member:
chat_member = ChatMembers(chat.chat_id, user.user_id)
SESSION.add(chat_member)
SESSION.commit()
my init.py file:
def start() -> scoped_session:
engine = create_engine(DB_URI, client_encoding="utf8")
BASE.metadata.bind = engine
BASE.metadata.create_all(engine)
return scoped_session(sessionmaker(bind=engine, autoflush=False))
BASE = declarative_base()
SESSION = start()
Tracelog:
sqlalchemy.exc.DataError: (psycopg2.errors.NumericValueOutOfRange) integer out of range
[SQL: INSERT INTO chat_members (chat, "user") VALUES (%(chat)s, %(user)s) RETURNING chat_members.priv_chat_id]
[parameters: {'chat': '-1001779301880', 'user': 5030277858}]
(Background on this error at: https://sqlalche.me/e/14/9h9h)

Categories