简体   繁体   English

FastAPI AttributeError:“用户”对象没有“密码”属性

[英]FastAPI AttributeError: 'User' object has no attribute 'password'

I have a FastAPI project in which I am using Async SQLAlchemy orm and postgresql as db.我有一个 FastAPI 项目,在该项目中我使用 Async SQLAlchemy orm 和 postgresql 作为数据库。 Basically it is a simple CRUD based Job Board I've manage to create the CRUD operations successfully and they are working as I expected.基本上它是一个简单的基于 CRUD 的工作板,我成功地创建了 CRUD 操作,并且它们按我的预期工作。 The issue I'm stumbled upon is user authentication I'm trying to implementing authentication via JWT on user registration, user will fill out the fields, username, email and password then an verification email will be sent to that user email to verify the JWT token after that is_active field will be True which is by default False .我偶然发现的问题是用户身份验证我试图在用户注册时通过 JWT 实施身份验证,用户将填写字段、用户名、电子邮件和密码,然后将向该用户电子邮件发送一封验证电子邮件以验证 JWT在is_active字段之后的标记将为True ,默认情况下为False I tried couple of ways but couldn't succeed, I'm having difficulty adding the user to the database.我尝试了几种方法但没有成功,我很难将用户添加到数据库中。

routes/route_user.py:路线/route_user.py:

from fastapi import APIRouter, HTTPException, status
from fastapi import Depends
from jose import jwt

from db.models.users import User
from schemas.users import UserCreate, ShowUser
from db.repository.users_data_access_layer import Users
from core.auth import Auth
from core.hashing import Hasher
from core.mailer import Mailer
from core.config import Settings
from depends import get_user_db

router = APIRouter()

get_settings = Settings()


@router.post("/", response_model=ShowUser)
async def create_user(form_data: UserCreate = Depends(), users: Users = Depends(get_user_db)):
    if await users.check_user(email=form_data.email) is not None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="User already exists"
        )
    elif await users.check_username(username=form_data.username) is not None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Username already exists"
        )

    new_user = User(email=form_data.email,
                username=form_data.username,
                hashed_password=Auth.get_password_hash(form_data.password)
                )
    await users.register_user(new_user)
    print(new_user)
    confirmation = Auth.get_confirmation_token(new_user.id)
    print(confirmation)
    new_user.confirmation = confirmation["jti"]


    try:
        Mailer.send_confirmation_message(confirmation["token"], form_data.email)
    except ConnectionRefusedError:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Email couldn't be send. Please try again."
        )
    return await users.register_user(form_data)

@router.get("/verify/{token}")
async def verify(token: str, users: Users = Depends(get_user_db)):
    invalid_token_error = HTTPException(status_code=400, detail="Invalid Token")
    try:
        payload = jwt.decode(token, get_settings.SECRET_KEY, algorithms=[get_settings.TOKEN_ALGORITHM])
        print(payload['sub'])
    except jwt.JWSError:
        raise HTTPException(status_code=403, detail="Token has Expired")
    if payload['scope'] != 'registration':
        raise invalid_token_error
    print(payload['sub'])
    user = await users.get_user_by_id(id=payload['sub'])
    print(user)
    print('hello2')
    if not user or await users.get_confirmation_uuid(str(User.confirmation)) != payload['jti']:
        print('hello')
        raise invalid_token_error
    if user.is_active:
        print('hello2')
        raise HTTPException(status_code=403, detail="User already Activated")
    user.confirmation = None
    user.is_active = True
    return await users.register_user(user)

the route above outputs the Attribute error:上面的路由输出属性错误:

File ".\db\repository\users_data_access_layer.py", line 26, in register_user
    hashed_password=user.password,
AttributeError: 'User' object has no attribute 'password'

user_data_access_layer.py user_data_access_layer.py

This is where all db communications are happening.这是所有数据库通信发生的地方。 Here I think I need some kind of save method to add to db for convenience but I don't know how to implement it.在这里,我想我需要某种save method来方便地添加到数据库中,但我不知道如何实现它。 I tried something like this:我试过这样的事情:

from core.hashing import Hasher
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import select
from sqlalchemy.sql import exists


from db.models.users import User
from schemas.users import UserCreate
from core.hashing import Hasher


db_session = Session

class Users():
    
    def __init__(self, db_session: Session):
        self.db_session = db_session
    async def save(self):
        if self.id == None:
           self.db_session.add(self)
        return await self.db_session.flush()
    
            #print('user created')

    async def register_user(self, user: UserCreate):
        new_user = User(username=user.username,
        email=user.email,
        hashed_password=user.password,
        is_active = False,
        is_superuser=False
        )
        self.db_session.add(new_user)
        await self.db_session.flush()
        return new_user

    async def check_user(self, email: str):
        user_exist = await self.db_session.execute(select(User).filter(User.email==email))
        #print(user_exist)
        return user_exist.scalar_one_or_none()

    async def check_username(self, username: str):
        user_exist = await self.db_session.execute(select(User).filter(User.username==username))
        #print(user_exist)
        return user_exist.scalar_one_or_none()
    
    async def get_user_by_id(self, id: int):
        user_exist = await self.db_session.execute(select(User).filter(User.id==id)
        #print(user_exist)
        return user_exist.scalar_one_or_none()

    async def get_confirmation_uuid(self, confirmation_uuid:str):
        user_exist = await self.db_session.execute(select(User).filter(str(User.confirmation)==confirmation_uuid))
        #print(user_exist)
        return user_exist

schemas/users.py模式/用户.py

from typing import Optional
from pydantic import BaseModel, EmailStr


class UserBase(BaseModel):
    username: str 
    email: EmailStr
    password: str


class UserCreate(UserBase):
    username: str
    email: EmailStr
    password: str

class ShowUser(UserBase):
    username: str
    email: EmailStr
    is_active: bool

    class Config():
        orm_mode = True

models/users.py模型/用户.py

import uuid
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship

from db.base_class import Base


class User(Base):
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    username = Column(String, unique=True, nullable=False)
    email = Column(String, nullable=False, unique=True, index=True)
    hashed_password = Column(String(255), nullable=False)
    is_active = Column(Boolean, default=False)
    is_superuser = Column(Boolean, default=False)
    confirmation = Column(UUID(as_uuid=True), nullable=True, default=uuid.uuid4)
    jobs = relationship("Job", back_populates="owner")

depends.py依赖.py

from db.session import async_session
from db.repository.jobs_data_access_layer import JobBoard
from db.repository.users_data_access_layer import Users

async def get_job_db():
    async with async_session() as session:
        async with session.begin():
            yield JobBoard(session)

async def get_user_db():
    async with async_session() as session:
        async with session.begin():
            yield Users(session)

Since this is all new stuff and wherever I reached I hit a wall and I'm working on this project for weeks now and couldn't find my way around it yet so any assistance would be appreciate.由于这是全新的东西,无论我走到哪里,我都会遇到一堵墙,我现在已经在这个项目上工作了几个星期,但还没有找到解决方法,所以任何帮助都将不胜感激。

There are different problems in the code.代码中有不同的问题。 Firstly some calls to the method of the class model/Users are called with the wrong parameters.首先,对类模型/用户的方法的一些调用是使用错误的参数调用的。 Indeed, some are called with a User object as parameter while those are expecting a Pydantic UserCreate model.实际上,有些是使用 User 对象作为参数调用的,而有些则需要 Pydantic UserCreate 模型。 So, when you send a User object instead of the Pydantic model, the password attribute does not exist.因此,当您发送User对象而不是 Pydantic 模型时, password属性不存在。 Secondly, afterwards, other problems will appear, since your methods to retrieve a User object actually return a list (ChunkIterator).其次,之后会出现其他问题,因为您检索 User 对象的方法实际上返回了一个列表(ChunkIterator)。 However, you make comparisons as if you were receiving an object.但是,您进行比较时就好像您正在接收一个对象。

I took the liberty of proposing an alternative by reformulating some of your code.我冒昧地通过重新编写您的一些代码来提出替代方案。

Now, I have created methods to save eventual modification of users in your DB, and created some methods that return a user according to different criteria (id, username, email) except that contrary to your code, those return an object (or None) instead of a list.现在,我创建了一些方法来保存数据库中用户的最终修改,并创建了一些根据不同标准(id、用户名、电子邮件)返回用户的方法,除了与您的代码相反,那些返回对象(或无)而不是列表。

user_data_access_layer.py user_data_access_layer.py

from fastapi import HTTPException, status

from db.models.users import User
from schemas.users import UserCreate
from db_config import SESSION
from auth import Auth

class Users():

    def __init__(self):
        pass

    @classmethod
    async def save(cls, user_instance):
        try:
            SESSION.add(user_instance)
            SESSION.commit()
        except Exception as error:
            SESSION.rollback()
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)

    @classmethod
    async def get_user_by_id(cls, id):
        user = await SESSION.query(User).filter(User.id == id).one_or_none()
        return user

    @classmethod
    async def get_user_by_username(cls, username):
        user = await SESSION.query(User).filter(User.username == username).one_or_none()
        return user

    @classmethod
    async def get_user_by_email(cls, email):
        user = await SESSION.query(User).filter(User.email == email).one_or_none()
        return user

    @classmethod
    async def get_user_by_confirmation(cls, confirmation):
        user = await SESSION.query(User).filter(User.confirmation == confirmation).one_or_none()
        return user

    @classmethod
    async def create_user(self, user: UserCreate):
        new_user = User(username=user.username,
                        email=user.email,
                        hashed_password=Auth.get_password_hash(user.password)
                        )
        cls.save(new_user)
        return new_user

As you can see, I removed the Session creation from your layer file and put it in a global variable, in a separate file.如您所见,我从您的图层文件中删除了 Session 创建,并将其放在一个单独文件中的全局变量中。

db_config.py db_config.py

from sqlalchemy import create_engine
from sqlalchemy.engine import Engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.session import Session

ENGINE: Engine = create_engine(your_config_url, pool_pre_ping=True)
SESSION: Session = sessionmaker(bind=ENGINE)()

And to conclude, here is your endpoint updated according to the proposed code.总而言之,这是根据建议的代码更新的端点。 I have added comments inside it to make it easier to understand.我在其中添加了注释以使其更易于理解。

route.py路线.py

@router.post("/", response_model=ShowUser)
async def create_user(form_data: UserCreate = Depends(), users: Users = Depends(get_user_db)):
    # CHECK IF USER ALREADY EXISTS
    if await Users.get_user_by_email(email=form_data.email) is not None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="User already exists"
        )
    # CHECK IF USERNAME ALREADY EXISTS
    elif await Users.get_user_by_username(username=form_data.username) is not None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Username already exists"
        )

    # CREATE USER WITH USERS METHOD
    # now the hashing of the password is done directly in the creation method
    new_user = await Users.create_user(form_data)
    
    # GET TOKEN
    # we no longer create a new uid for JTI, but use the one created automatically during user creation
    # so I modified the get_confirmation_token function so that it takes the user's JTI uid as a parameter
    confirmation_token = Auth.get_confirmation_token(
                            new_user.id,
                            new_user.confirmation)

table has not password, table has only hashed_password表没有密码,表只有 hashed_pa​​ssword

new_user = User(username=user.username,
email=user.email,
hashed_password=user.hashed_password,
is_active = False,
is_superuser=False
)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 AttributeError: 'Customer' object 没有属性 'json' fastapi - AttributeError: 'Customer' object has no attribute 'json' fastapi AttributeError:“博客”object 没有属性“项目”-FastAPI - AttributeError: 'Blog' object has no attribute 'items' - FastAPI AttributeError: 'FastAPI' 对象没有属性 'logger' - AttributeError: 'FastAPI' object has no attribute 'logger' AttributeError: Cutome 用户 model object 没有属性“密码” - AttributeError: Cutome User model object has no attribute 'password' AttributeError: 'User' 对象没有属性 'check_password_correction' - AttributeError: 'User' object has no attribute 'check_password_correction' SqlModel:Fastapi AttributeError:类型 object 'AddressBaseCore' 没有属性 '__config__' - SqlModel : Fastapi AttributeError: type object 'AddressBaseCore' has no attribute '__config__' AttributeError:“ NoneType”对象没有属性“ password” - AttributeError: 'NoneType' object has no attribute 'password' Django AttributeError: 'User' object 没有属性 'set_password' 但用户未被覆盖 - Django AttributeError: 'User' object has no attribute 'set_password' but user is not override AttributeError:与 User.password_hash 关联的“InstrumentedAttribute”object 和“Comparator”object 都没有属性“count” - AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with User.password_hash has an attribute 'count' AttributeError 'User' 对象没有属性 'userStripe' - AttributeError 'User' object has no attribute 'userStripe'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM