Mark-Lasfar commited on
Commit
8c49d21
·
1 Parent(s): 7f03ffe

Fix circular import by moving CustomSQLAlchemyUserDatabase to database.py and removing user_db.py

Browse files
Files changed (4) hide show
  1. api/auth.py +2 -3
  2. api/database.py +32 -8
  3. api/user_db.py +0 -51
  4. init_db.py +5 -1
api/auth.py CHANGED
@@ -1,6 +1,6 @@
1
  # api/auth.py
2
  # SPDX-FileCopyrightText: Hadad <[email protected]>
3
- # SPDX-License-Identifier: Apache-2.0
4
 
5
  from fastapi_users import FastAPIUsers
6
  from fastapi_users.authentication import CookieTransport, JWTStrategy, AuthenticationBackend
@@ -17,8 +17,7 @@ import os
17
  import logging
18
  import secrets
19
 
20
- from api.user_db import CustomSQLAlchemyUserDatabase, get_user_db # استيراد من user_db.py
21
- from api.database import User, OAuthAccount
22
  from api.models import UserRead, UserCreate, UserUpdate
23
 
24
  # إعداد اللوقينج
 
1
  # api/auth.py
2
  # SPDX-FileCopyrightText: Hadad <[email protected]>
3
+ # SPDX-License-License: Apache-2.0
4
 
5
  from fastapi_users import FastAPIUsers
6
  from fastapi_users.authentication import CookieTransport, JWTStrategy, AuthenticationBackend
 
17
  import logging
18
  import secrets
19
 
20
+ from api.database import User, OAuthAccount, CustomSQLAlchemyUserDatabase, get_user_db # استيراد من database.py
 
21
  from api.models import UserRead, UserCreate, UserUpdate
22
 
23
  # إعداد اللوقينج
api/database.py CHANGED
@@ -1,29 +1,29 @@
1
  # api/database.py
2
  # SPDX-FileCopyrightText: Hadad <[email protected]>
3
- # SPDX-License-Identifier: Apache-2.0
4
 
5
  import os
6
  import logging
7
  from datetime import datetime
8
- from typing import AsyncGenerator
9
 
10
- from sqlalchemy import Column, String, Integer, ForeignKey, DateTime, Boolean, Text
11
  from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
12
  from sqlalchemy.ext.declarative import declarative_base
13
  from sqlalchemy.orm import relationship
14
  from fastapi import Depends
 
15
  import aiosqlite
16
- from api.user_db import CustomSQLAlchemyUserDatabase, get_user_db # استيراد من user_db.py
17
 
18
  # إعداد اللوج
19
  logger = logging.getLogger(__name__)
20
 
21
- # استخدم القيمة مباشرة إذا لم يكن هناك متغير بيئة
22
  SQLALCHEMY_DATABASE_URL = os.environ.get(
23
  "SQLALCHEMY_DATABASE_URL"
24
  ) or "sqlite+aiosqlite:///./data/mgzon_users.db"
25
 
26
- # تأكد أن الدرايفر async
27
  if "aiosqlite" not in SQLALCHEMY_DATABASE_URL:
28
  raise ValueError("Database URL must use 'sqlite+aiosqlite' for async support")
29
 
@@ -58,7 +58,7 @@ class OAuthAccount(Base):
58
 
59
  user = relationship("User", back_populates="oauth_accounts", lazy="selectin")
60
 
61
- class User(Base): # إزالة SQLAlchemyBaseUserTable لأن fastapi-users بيستخدم CustomSQLAlchemyUserDatabase
62
  __tablename__ = "user"
63
  id = Column(Integer, primary_key=True, index=True)
64
  email = Column(String, unique=True, index=True, nullable=False)
@@ -99,6 +99,30 @@ class Message(Base):
99
 
100
  conversation = relationship("Conversation", back_populates="messages")
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  # دالة لجلب الجلسة async
103
  async def get_db() -> AsyncGenerator[AsyncSession, None]:
104
  async with AsyncSessionLocal() as session:
@@ -111,7 +135,7 @@ async def get_db() -> AsyncGenerator[AsyncSession, None]:
111
  async def get_user_db(session: AsyncSession = Depends(get_db)) -> AsyncGenerator[CustomSQLAlchemyUserDatabase, None]:
112
  yield CustomSQLAlchemyUserDatabase(session, User, OAuthAccount)
113
 
114
- # (اختياري) دالة لإنشاء الجداول
115
  async def init_db():
116
  try:
117
  async with async_engine.begin() as conn:
 
1
  # api/database.py
2
  # SPDX-FileCopyrightText: Hadad <[email protected]>
3
+ # SPDX-License-License: Apache-2.0
4
 
5
  import os
6
  import logging
7
  from datetime import datetime
8
+ from typing import AsyncGenerator, Optional, Dict, Any
9
 
10
+ from sqlalchemy import Column, String, Integer, ForeignKey, DateTime, Boolean, Text, select
11
  from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
12
  from sqlalchemy.ext.declarative import declarative_base
13
  from sqlalchemy.orm import relationship
14
  from fastapi import Depends
15
+ from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase
16
  import aiosqlite
 
17
 
18
  # إعداد اللوج
19
  logger = logging.getLogger(__name__)
20
 
21
+ # استخدم القيمة مباشرة إذا لم يكن هناك متغير بيئة
22
  SQLALCHEMY_DATABASE_URL = os.environ.get(
23
  "SQLALCHEMY_DATABASE_URL"
24
  ) or "sqlite+aiosqlite:///./data/mgzon_users.db"
25
 
26
+ # تأكد أن الدرايفر async
27
  if "aiosqlite" not in SQLALCHEMY_DATABASE_URL:
28
  raise ValueError("Database URL must use 'sqlite+aiosqlite' for async support")
29
 
 
58
 
59
  user = relationship("User", back_populates="oauth_accounts", lazy="selectin")
60
 
61
+ class User(SQLAlchemyBaseUserTable[int], Base):
62
  __tablename__ = "user"
63
  id = Column(Integer, primary_key=True, index=True)
64
  email = Column(String, unique=True, index=True, nullable=False)
 
99
 
100
  conversation = relationship("Conversation", back_populates="messages")
101
 
102
+ # قاعدة بيانات المستخدم المخصصة (نقلناها من user_db.py)
103
+ class CustomSQLAlchemyUserDatabase(SQLAlchemyUserDatabase[User, int]):
104
+ """
105
+ قاعدة بيانات مخصَّصة لمكتبة fastapi-users.
106
+ تضيف طريقة parse_id التي تُحوِّل الـ ID من str → int.
107
+ """
108
+ def parse_id(self, value: Any) -> int:
109
+ logger.debug(f"Parsing user id: {value} (type={type(value)})")
110
+ return int(value) if isinstance(value, str) else value
111
+
112
+ async def get_by_email(self, email: str) -> Optional[User]:
113
+ logger.info(f"Looking for user with email: {email}")
114
+ stmt = select(self.user_table).where(self.user_table.email == email)
115
+ result = await self.session.execute(stmt)
116
+ return result.scalar_one_or_none()
117
+
118
+ async def create(self, create_dict: Dict[str, Any]) -> User:
119
+ logger.info(f"Creating new user: {create_dict.get('email')}")
120
+ user = self.user_table(**create_dict)
121
+ self.session.add(user)
122
+ await self.session.commit()
123
+ await self.session.refresh(user)
124
+ return user
125
+
126
  # دالة لجلب الجلسة async
127
  async def get_db() -> AsyncGenerator[AsyncSession, None]:
128
  async with AsyncSessionLocal() as session:
 
135
  async def get_user_db(session: AsyncSession = Depends(get_db)) -> AsyncGenerator[CustomSQLAlchemyUserDatabase, None]:
136
  yield CustomSQLAlchemyUserDatabase(session, User, OAuthAccount)
137
 
138
+ # دالة لإنشاء الجداول
139
  async def init_db():
140
  try:
141
  async with async_engine.begin() as conn:
api/user_db.py DELETED
@@ -1,51 +0,0 @@
1
- # api/user_db.py
2
- # SPDX-FileCopyrightText: Hadad <[email protected]>
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- import logging
6
- from typing import Any, AsyncGenerator, Dict, Optional
7
-
8
- from fastapi import Depends
9
- from fastapi_users.db import SQLAlchemyUserDatabase
10
- from sqlalchemy.ext.asyncio import AsyncSession
11
- from sqlalchemy import select
12
-
13
- from api.database import User, OAuthAccount # استيراد جداولك
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
- class CustomSQLAlchemyUserDatabase(SQLAlchemyUserDatabase[User, int]):
18
- """
19
- قاعدة بيانات مخصَّصة لمكتبة fastapi‑users.
20
- تضيف طريقة parse_id التي تُحوِّل الـ ID من str → int.
21
- """
22
-
23
- def parse_id(self, value: Any) -> int:
24
- logger.debug(f"Parsing user id: {value} (type={type(value)})")
25
- # إذا كان الـ ID نصًا (من JWT) → حوّله إلى int
26
- return int(value) if isinstance(value, str) else value
27
-
28
- # ---------- وظائف مساعدة ----------
29
- async def get_by_email(self, email: str) -> Optional[User]:
30
- logger.info(f"Looking for user with email: {email}")
31
- stmt = select(self.user_table).where(self.user_table.email == email)
32
- result = await self.session.execute(stmt)
33
- return result.scalar_one_or_none()
34
-
35
- async def create(self, create_dict: Dict[str, Any]) -> User:
36
- logger.info(f"Creating new user: {create_dict.get('email')}")
37
- user = self.user_table(**create_dict)
38
- self.session.add(user)
39
- await self.session.commit()
40
- await self.session.refresh(user)
41
- return user
42
-
43
- # ---------- Dependency يُستَخدم في باقي المشروع ----------
44
- async def get_user_db(
45
- session: AsyncSession = Depends(lambda: None) # سيتم استبداله في database.py
46
- ) -> AsyncGenerator[CustomSQLAlchemyUserDatabase, None]:
47
- """
48
- يُستَخدم كـ Depends في جميع المسارات التي تحتاج إلى قاعدة بيانات المستخدم.
49
- سيتم تمرير الـ AsyncSession الفعلي من `api/database.py`.
50
- """
51
- yield CustomSQLAlchemyUserDatabase(session, User, OAuthAccount)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
init_db.py CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  import os
2
  import logging
3
  import asyncio
@@ -6,7 +10,7 @@ from sqlalchemy import select, delete
6
  from api.database import async_engine, Base, User, OAuthAccount, Conversation, Message, AsyncSessionLocal
7
  from passlib.context import CryptContext
8
 
9
- # Setup logging
10
  logging.basicConfig(level=logging.INFO)
11
  logger = logging.getLogger(__name__)
12
 
 
1
+ # init_db.py
2
+ # SPDX-FileCopyrightText: Hadad <[email protected]>
3
+ # SPDX-License-License: Apache-2.0
4
+
5
  import os
6
  import logging
7
  import asyncio
 
10
  from api.database import async_engine, Base, User, OAuthAccount, Conversation, Message, AsyncSessionLocal
11
  from passlib.context import CryptContext
12
 
13
+ # إعداد اللوج
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger(__name__)
16