diff --git a/alembic.ini b/alembic.ini index e041d95..3bbc0cf 100644 --- a/alembic.ini +++ b/alembic.ini @@ -4,8 +4,9 @@ # path to migration scripts script_location = migrations -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s # sys.path path, will be prepended to sys.path if present. # defaults to the current working directory. @@ -48,11 +49,16 @@ prepend_sys_path = . # version_path_separator = space version_path_separator = os # Use os.pathsep. Default configuration used for new projects. +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + # the output encoding used when revision files # are written from script.py.mako # output_encoding = utf-8 -# sqlalchemy.url = driver://user:pass@localhost/dbname +; sqlalchemy.url = driver://user:pass@localhost/dbname [post_write_hooks] @@ -66,6 +72,12 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # black.entrypoint = black # black.options = -l 79 REVISION_SCRIPT_FILENAME +# lint with attempts to fix using "ruff" - use the exec runner, execute a binary +# hooks = ruff +# ruff.type = exec +# ruff.executable = %(here)s/.venv/bin/ruff +# ruff.options = --fix REVISION_SCRIPT_FILENAME + # Logging configuration [loggers] keys = root,sqlalchemy,alembic diff --git a/back/api.py b/back/api.py index 307e55a..cfa585e 100644 --- a/back/api.py +++ b/back/api.py @@ -103,7 +103,7 @@ async def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form( temp_ancmt = orm_models.Announcement(user_id=current_user.id, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, metro=metro, trashId=trashId, src=uploaded_name, booked_by=0) - await db.add(temp_ancmt) # добавляем в бд + db.add(temp_ancmt) # добавляем в бд await db.commit() # сохраняем изменения await db.refresh(temp_ancmt) # обновляем состояние объекта return {"Answer" : True} @@ -227,12 +227,14 @@ async def add_points(user_id: int, db: Annotated[Session, Depends(auth_utils.get @app.get("/api/user/poem", response_model=pydantic_schemas.Poem) async def poems_to_front(db: Annotated[Session, Depends(auth_utils.get_session)]): #num_of_poems = db.query(orm_models.Poems).count() # определяем кол-во стихов в бд - num_of_poems = await db.execute(select(orm_models.Poems)).scalars().count() # определяем кол-во стихов в бд + query = await db.execute(select(orm_models.Poems)) # определяем кол-во стихов в бд + num_of_poems = len(query.scalars().all()) # если стихов в бд нет if num_of_poems < 1: add_poems_and_filters.add_poems_to_db(db) # добавляем поэмы в базу данных - num_of_poems = await db.execute(select(orm_models.Poems)).scalars().count() # определяем кол-во стихов в бд - #num_of_poems = db.query(orm_models.Poems).count() # определяем кол-во стихов в бд + # после добавления стихов снова определяем кол-во стихов в бд + query = await db.execute(select(orm_models.Poems)) + num_of_poems = len(query.scalars().all()) rand_id = random.randint(1, num_of_poems) # генерируем номер стихотворения #poem = db.query(orm_models.Poems).filter(orm_models.Poems.id == rand_id).first() # находим стих в бд query_poem = await db.execute(select(orm_models.Poems).where(orm_models.Poems.id == rand_id)) # находим стих в бд @@ -246,7 +248,7 @@ async def poems_to_front(db: Annotated[Session, Depends(auth_utils.get_session)] async def get_trashboxes(data: pydantic_schemas.TrashboxRequest = Depends()):#крутая функция для работы с api # json, передаваемый стороннему API BASE_URL= "https://geointelect2.gate.petersburg.ru" - my_token="eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3ODYyMjUzMzMsImlhdCI6MTY5MTUzMDkzMywianRpIjoiYjU0MmU3MTQtYzJkMS00NTY2LWJkY2MtYmQ5NzA0ODY1ZjgzIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjJhOTgwMzUyLTY1M2QtNGZlZC1iMDI1LWQ1N2U0NDRjZmM3NiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIyYTk4MDM1Mi02NTNkLTRmZWQtYjAyNS1kNTdlNDQ0Y2ZjNzYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.FTKiC1hpWcOkmSW9QZpC-RY7Ko50jw1mDMfXIWYxlQ-zehLm2CLmOnHvYoOoI39k2OzeCIAB9ZdRrrGZc6G9Z1eFELUjNGEqKxSC1Phj9ATemKgbOKEttk-OGc-rFr9VPA8_SnfvLts6wTI2YK33YBIxCF5nCbnr4Qj3LeEQ0d6Hy8PO4ATrBF5EOeuAZRprvIEjXe_f8N9ONKckCPB-xFB4P2pZlVXGoCNoewGEcY3zXH4khezN6zcVr6tpc6G8dBv9EqT_v92IDSg-aXQk6ysA0cO0-6x5w1-_qU0iHGIAPsLNV9IKBoFbjc0JH6cWabldPRH12NP1trvYfqKDGQ" + my_token="eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3ODgzODM1NzQsImlhdCI6MTY5MzY4OTE3NCwianRpIjoiNTIxNTE4ODYtZjJiMy00MWQ2LWE2OTYtN2JjNjg1YzRmZDBjIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjMyNmZlYjNjLWJhMDktNDU4NC05M2JiLTgzZjMyMTRjOTdkNSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIzMjZmZWIzYy1iYTA5LTQ1ODQtOTNiYi04M2YzMjE0Yzk3ZDUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.SpFC8EufKYV4tHWSvaeypZ-j-Lqhpva3tvilMba_Yh79EzT1EUOomeT5pC7PNJPlfsuXvOyutPBXG9Fpo7tazY2K5ymxQubYrsYt1m6KrPAJYAvDVGiZl5762-7zH1NoENgxLOjNc8eqN_cZBlcqImPyGiGAIqXaZ_Bwns-Jx5qHrYipFOQ3yXtehvy8-l84ZFhO0VhUHoZZnlfXz5x3OuOTVLU1-NjUclZEYc7t3ZlOl5m-r8jAfAdTnY-V-JDfLMbwqjouAAzauUNm4-1uiZjntrShuBsYoJcVaXcW8cSWnVk5NgWQNxwHoel4AhGIGBWPQ6gmJw2jkXBBjzXbqA" head = {'Authorization': 'Bearer {}'.format(my_token)} # Данные пользователя (местоположение, количество мусорок, которое пользователь хочет видеть) my_data={ @@ -274,7 +276,7 @@ async def get_trashboxes(data: pydantic_schemas.TrashboxRequest = Depends()):#к list_of_category = ["Металл", "Бумага", "Стекло", "Иное", "Тетра Пак", "Батарейки", "Крышечки", "Шины", "Опасные отходы", "Лампочки", "Пластик"] # Получение ответа от стороннего апи - response = await requests.post(f"{BASE_URL}/nearest_recycling/get", headers=head, data=my_data, timeout=10) + response = requests.post(f"{BASE_URL}/nearest_recycling/get", headers=head, data=my_data, timeout=10) infos = response.json() # Чтение ответа trashboxes = [] @@ -292,7 +294,7 @@ async def get_trashboxes(data: pydantic_schemas.TrashboxRequest = Depends()):#к for a in list_of_category: if a in temp_dict["Categories"] and temp_dict not in trashboxes: trashboxes.append(temp_dict) - uniq_trashboxes = await [pydantic_schemas.TrashboxResponse(**ast.literal_eval(el1)) for el1 in set([str(el2) for el2 in trashboxes])] + uniq_trashboxes = [pydantic_schemas.TrashboxResponse(**ast.literal_eval(el1)) for el1 in set([str(el2) for el2 in trashboxes])] return uniq_trashboxes @@ -314,7 +316,7 @@ async def dispose(data: pydantic_schemas.DisposeRequest, current_user_schema: An new_trashox = orm_models.Trashbox(user_id=current_user.id, date_of_choice=datetime.date.today(), name=data_trashbox.Name, latitude=data_trashbox.Lat, longtitude=data_trashbox.Lng, address=data_trashbox.Address, category=data_trashbox.Category) # добавляем в бд - await db.add(new_trashox) + db.add(new_trashox) # в соответствии с логикой api, после утилизации объявление пользователя удаляется # находим объявление с айди data.ann_id #ann_to_del = db.query(orm_models.Announcement).filter(orm_models.Announcement.id == data.ann_id).first() # diff --git a/back/orm_models.py b/back/orm_models.py index c866d6f..7d43413 100644 --- a/back/orm_models.py +++ b/back/orm_models.py @@ -17,8 +17,8 @@ class User(Base):#класс пользователя num_of_ratings = Column(Integer, default=0) # количество оценок (т.е. то, сколько раз другие пользователи оценили текущего) reg_date = Column(Date) # дата регистрации - announcements = relationship("Announcement", back_populates="user") - trashboxes_chosen = relationship("Trashbox", back_populates="user") + announcements = relationship("Announcement", back_populates="user", lazy='selectin') + trashboxes_chosen = relationship("Trashbox", back_populates="user", lazy='selectin') class Announcement(Base): #класс объявления __tablename__ = "announcements" diff --git a/migrations/README b/migrations/README index 98e4f9c..e0d0858 100644 --- a/migrations/README +++ b/migrations/README @@ -1 +1 @@ -Generic single-database configuration. \ No newline at end of file +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/migrations/env.py b/migrations/env.py index bfa8186..7523a7e 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -1,11 +1,12 @@ +import asyncio from logging.config import fileConfig -from sqlalchemy import engine_from_config from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config from alembic import context - from back import auxiliary_for_alembic, db # this is the Alembic Config object, which provides @@ -17,6 +18,10 @@ config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name) +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata target_metadata = auxiliary_for_alembic.Base.metadata # other values from the config, defined by the needs of env.py, @@ -25,7 +30,7 @@ target_metadata = auxiliary_for_alembic.Base.metadata # ... etc. -def run_migrations_offline(): +def run_migrations_offline() -> None: """Run migrations in 'offline' mode. This configures the context with just a URL @@ -37,44 +42,49 @@ def run_migrations_offline(): script output. """ - # url = config.get_main_option("sqlalchemy.url") url = config.get_main_option(db.SQLALCHEMY_DATABASE_URL) context.configure( url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"}, - render_as_batch=True ) with context.begin_transaction(): context.run_migrations() -def run_migrations_online(): - """Run migrations in 'online' mode. +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) - In this scenario we need to create an Engine + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine and associate a connection with the context. """ + configuration = config.get_section(config.config_ini_section) configuration['sqlalchemy.url'] = db.SQLALCHEMY_DATABASE_URL - connectable = engine_from_config( + connectable = async_engine_from_config( configuration, prefix="sqlalchemy.", poolclass=pool.NullPool, ) - with connectable.connect() as connection: - context.configure( - connection=connection, - target_metadata=target_metadata, - render_as_batch=True - ) + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) - with context.begin_transaction(): - context.run_migrations() + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) if context.is_offline_mode(): diff --git a/migrations/script.py.mako b/migrations/script.py.mako index 2c01563..fbc4b07 100644 --- a/migrations/script.py.mako +++ b/migrations/script.py.mako @@ -5,20 +5,22 @@ Revises: ${down_revision | comma,n} Create Date: ${create_date} """ +from typing import Sequence, Union + from alembic import op import sqlalchemy as sa ${imports if imports else ""} # revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} -def upgrade(): +def upgrade() -> None: ${upgrades if upgrades else "pass"} -def downgrade(): +def downgrade() -> None: ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/8e631a2fe6b8_lazy_selectin_added_to_user_table_.py b/migrations/versions/8e631a2fe6b8_lazy_selectin_added_to_user_table_.py new file mode 100644 index 0000000..736e5a3 --- /dev/null +++ b/migrations/versions/8e631a2fe6b8_lazy_selectin_added_to_user_table_.py @@ -0,0 +1,30 @@ +"""lazy=selectin added to user table relationships + +Revision ID: 8e631a2fe6b8 +Revises: +Create Date: 2023-09-02 23:45:08.799366 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '8e631a2fe6b8' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ###