本文介紹了FastAPI+Tortoise ORM+FastAPI用戶(Python)-關系-多對多的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!
問題描述
我正在使用FastAPI、Tortoise ORM和FastAPI用戶制作一個API來學習。基本上,我從OpenWeather API獲取城市天氣數據,并使用Tortoise ORM存儲在SQLite數據庫中。我對FastAPI用戶進行了身份驗證。
我使用的是外鍵,關系運行良好。但現在我想要一些改變(一種改進)。我希望每個用戶都有他們的項目,并且在登錄并訪問終結點(@router.get("/me/onlyitems/", response_model=List[schemas.Card_Pydantic])
)之后,它只接收他們的項目。
它工作得很好,我只接收用戶項(使用Owner_id),但我的項表使用外部唯一ID(項是一個城市,并且每個城市都有一個唯一ID),并且它是主鍵。
但是,當另一個用戶嘗試添加相同的城市時,它會給出一個錯誤,因為表中不能有相同的城市,即使是與另一個所有者。嗯,應該是這樣的.但我想讓其他用戶訪問相同的城市(他們有相同的信息,他們不是定制的)。
然后,我希望多個用戶能夠訪問同一項目(每個用戶可以訪問多個項目)。
我認為為每個用戶重復項目信息(使用另一個ID)并不理想,不同的用戶訪問同一項目會更好。
我認為這將是一個多對多的關系,但我不知道如何建立這種關系并使用FastAPI獲得它。
我想過創建一個只有UserID和CityID(以及一個自動遞增ID)的中間表,并使用它來獲取相關數據,但我不知道這是最好的方法還是可行的。
我嘗試向Tortoise文檔學習,但我找不到我需要的部分,也無法理解文檔中的一些復雜示例和部分內容。
我使用的是包的更新版本和Python3.9.4
好的,讓我展示一些代碼:
Models.py
class UserModel(TortoiseBaseUserModel): # From FastAPI Users
cards: fields.ReverseRelation["Card"]
class OAuthAccountModel(TortoiseBaseOAuthAccountModel): # From FastAPI Users
user = fields.ForeignKeyField("models.UserModel", related_name="oauth_accounts")
class User(models.BaseUser, models.BaseOAuthAccountMixin): # From FastAPI Users
pass
class UserCreate(models.BaseUserCreate): # From FastAPI Users
pass
class UserUpdate(User, models.BaseUserUpdate): # From FastAPI Users
pass
class UserDB(User, models.BaseUserDB, PydanticModel): # From FastAPI Users
class Config:
orm_mode = True
orig_model = UserModel
user_db = TortoiseUserDatabase(UserDB, UserModel, OAuthAccountModel) # From FastAPI Users
class Card(tmodels.Model):
"""
The Card model, related to the user.
"""
id = fields.IntField(pk=True)
city_name = fields.CharField(max_length=30)
temperature = fields.FloatField()
state = fields.CharField(max_length=30, null=True)
icon = fields.CharField(max_length=3, null=True)
period = fields.CharField(max_length=20, null=True)
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
owner: fields.ForeignKeyRelation[UserModel] = fields.ForeignKeyField(
"models.UserModel", related_name="cards")
class PydanticMeta:
exclude = ["owner"]
我嘗試更改我的所有者字段及其與UserModel的關系:
class Card(tmodels.Model):
owner = fields.ManyToManyField("models.UserModel", related_name="cards")
class UserModel(TortoiseBaseUserModel):
cards: fields.ManyToManyRelation["Card"]
我不知道它是否正確,我無法使其工作,但問題可能出在其他地方。
我的用戶終結點創建項目:
@router.post("/", response_model=Card_Pydantic)
async def create_item_for_current_user(
card: CardCreate, user: UserDB = Depends(fastapi_users.get_current_active_user)):
card_obj = await crud.create_user_card(card=card, user=user)
if card_obj is None:
raise HTTPException(status_code=404, detail=f"City '{card.city_name}' not found")
return await card_obj
此處使用的CRUD函數(用于創建項目):
async def create_user_card(
card: schemas.CardCreate, user: UserDB = Depends(fastapi_users.get_current_active_user)):
card_json = await weather_api.get_api_info_by_name(card.city_name)
if card_json is None:
return None
card_obj = await Card.create(**card_json,
owner_id=user.id) # ** is used to pass the keys values as kwargs.
return await card_obj
我的終結點以獲取用戶擁有的項目:
@router.get("/me/onlyitems/", response_model=List[schemas.Card_Pydantic])
async def read_current_user_items(user: UserDB = Depends(fastapi_users.get_current_active_user)):
user_cards = await schemas.Card_Pydantic.from_queryset(Card.filter(owner_id=user.id).all())
return await crud.update_only_card_for(user_cards, user.id)
我的CRUD函數是:
async def update_only_card_for(user_cards, owner_id):
for city in user_cards:
await update_card(city.id, owner_id)
return await schemas.Card_Pydantic.from_queryset(Card.filter(owner_id=owner_id).all())
# The update function (it will check if it need to update or not)
async def update_card(city_id: int, owner_id: UUID = UserDB):
card_obj = await Card.get(id=city_id)
card_time = card_obj.updated_at
timezone = card_time.tzinfo
now = datetime.now(timezone)
time_to_update = now - timedelta(minutes=15)
if card_time < time_to_update:
card_json = await weather_api.get_api_info_by_id(city_id)
await card_obj.update_from_dict(card_json).save() # with save will update the update_at field
card_updated = await schemas.Card_Pydantic.from_queryset_single(Card.get(id=city_id))
return card_updated
如何更改才能獲得用戶需要訪問的卡?我認為我不能使用按Owner_id篩選(好的,如果我可以創建我的中間表,我可以,但不知道如何做和獲取數據),但是我不知道如何建立關系來將用戶鏈接到他添加的項目,同時其他用戶訪問相同的項目。
此代碼運行良好(我使用的是nuxt.js前端),直到有重復的城市…
處理它的最佳方式是什么?如何處理?
謝謝,迭戈。
推薦答案
我解決了。我認為Tortoise文檔有點令人困惑,但我找到了如何創建關系和檢索數據。
模型:(簡化版)
class Card(tmodels.Model):
id = fields.IntField(pk=True)
city_name = fields.CharField(max_length=30)
owners: fields.ManyToManyRelation["UserModel"] = fields.ManyToManyField(
"models.UserModel", related_name="cards", through="card_usermodel")
class UserModel(TortoiseBaseUserModel):
cards: fields.ManyToManyRelation[Card]
through="card_usermodel"
將使用卡ID和所有者ID創建中間表,它將定義關系。
進行數據操作:
async def create_user_card(
card: schemas.CardCreate, user: UserDB = Depends(fastapi_users.get_current_active_user)):
card_json = await weather_api.get_api_info_by_name(card.city_name)
if card_json is None:
return None
owner = await UserModel.get_or_none(id=user.id)
card_obj = await Card.update_or_create(**card_json)
await card_obj[0].owners.add(owner)
return await schemas.Card_Response_Pydantic.from_queryset_single(Card.get(id=card_json.get("id"), owners=user.id))
基本就是這樣。我還更改了一些平凡的模型,以驗證響應。
這篇關于FastAPI+Tortoise ORM+FastAPI用戶(Python)-關系-多對多的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,