rating routes added
This commit is contained in:
parent
cc414e38bd
commit
5fcee1157e
20
back/db.py
20
back/db.py
@ -1,8 +1,8 @@
|
|||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
from sqlalchemy import create_engine, select
|
from sqlalchemy import create_engine, select, MetaData
|
||||||
# from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
# from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||||
from sqlalchemy.orm import sessionmaker, Session
|
from sqlalchemy.orm import sessionmaker, Session, DeclarativeBase
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
@ -18,4 +18,18 @@ engine = create_engine(
|
|||||||
SessionLocal = sessionmaker(bind=engine, autoflush=True, autocommit=False)
|
SessionLocal = sessionmaker(bind=engine, autoflush=True, autocommit=False)
|
||||||
|
|
||||||
database = SessionLocal()
|
database = SessionLocal()
|
||||||
Base = declarative_base()
|
# Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# in your application's model:
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
metadata = MetaData(naming_convention={
|
||||||
|
"ix": "ix_%(column_0_label)s",
|
||||||
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||||
|
"ck": "ck_%(table_name)s_`%(constraint_name)s`",
|
||||||
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||||
|
"pk": "pk_%(table_name)s"
|
||||||
|
})
|
@ -2,4 +2,5 @@ from sqlalchemy import Table, MetaData
|
|||||||
from .db import engine
|
from .db import engine
|
||||||
|
|
||||||
tbl = Table('UserDatabase', MetaData(), autoload_with=engine)
|
tbl = Table('UserDatabase', MetaData(), autoload_with=engine)
|
||||||
tbl.drop(engine, checkfirst=False)
|
tbl.drop(engine, checkfirst=False)
|
||||||
|
a = input()
|
67
back/main.py
67
back/main.py
@ -19,7 +19,7 @@ import shutil
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from .db import Base, engine, SessionLocal, database
|
from .db import Base, engine, SessionLocal, database
|
||||||
from .service import add_poems_to_db, generate_poem
|
from .service import add_poems_to_db, generate_poem, check_obsolete
|
||||||
from . import schemas, models, utils
|
from . import schemas, models, utils
|
||||||
|
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
@ -36,32 +36,37 @@ app.mount("/uploads", StaticFiles(directory = "./uploads"))
|
|||||||
# # Записываем стихи в базу данных, если их еще нет (запускать только если стихов в базе нет).
|
# # Записываем стихи в базу данных, если их еще нет (запускать только если стихов в базе нет).
|
||||||
# add_poems_to_db(database)
|
# add_poems_to_db(database)
|
||||||
|
|
||||||
@app.get("/api/announcements")#адрес объявлений
|
@app.get("/api/announcements", response_model=List[schemas.Announcement]) #адрес объявлений
|
||||||
def annoncements_list(owner_id: int = None, metro: str = None, category: str = None, booked_by: int = 0):
|
def annoncements_list(params_to_sort: schemas.SortAnnouncements):
|
||||||
# Считываем данные из Body и отображаем их на странице.
|
# Считываем данные из Body и отображаем их на странице.
|
||||||
# В последствии будем вставлять данные в html-форму
|
# В последствии будем вставлять данные в html-форму
|
||||||
|
|
||||||
a = database.query(models.Announcement)
|
# Фильтруем по сроку годности
|
||||||
b = database.query(models.Announcement)
|
not_expired = check_obsolete(current_date=datetime.date.today())
|
||||||
c = database.query(models.Announcement)
|
# Фильтруем по другим параметрам и делаем пересечение с not_expired
|
||||||
d = database.query(models.Announcement)
|
result = not_expired.intersect(get_query_results(params_to_sort))
|
||||||
e = database.query(models.Announcement)
|
|
||||||
|
|
||||||
if owner_id != None:
|
|
||||||
b = a.filter(models.Announcement.owner_id == owner_id)
|
|
||||||
|
|
||||||
if metro != None:
|
# a = database.query(models.Announcement)
|
||||||
c = a.filter(models.Announcement.metro == metro)
|
# b = database.query(models.Announcement)
|
||||||
|
# c = database.query(models.Announcement)
|
||||||
|
# d = database.query(models.Announcement)
|
||||||
|
# e = database.query(models.Announcement)
|
||||||
|
|
||||||
if category != None:
|
# if owner_id != None:
|
||||||
d = a.filter(models.Announcement.category == category)
|
# b = a.filter(models.Announcement.owner_id == owner_id)
|
||||||
|
|
||||||
if not any([category, owner_id, metro]) and booked_by == -1:
|
# if metro != None:
|
||||||
result = a.all()
|
# c = a.filter(models.Announcement.metro == metro)
|
||||||
|
|
||||||
|
# if category != None:
|
||||||
|
# d = a.filter(models.Announcement.category == category)
|
||||||
|
|
||||||
|
# if not any([category, owner_id, metro]) and booked_by == 0:
|
||||||
|
# result = a.all()
|
||||||
|
|
||||||
else:
|
# else:
|
||||||
result = b.intersect(c, d, e).all()
|
# result = b.intersect(c, d, e).all()
|
||||||
|
|
||||||
return {"Success" : True, "list_of_announcements": result, "poem": generate_poem(database)}
|
return {"Success" : True, "list_of_announcements": result, "poem": generate_poem(database)}
|
||||||
|
|
||||||
|
|
||||||
@ -179,6 +184,25 @@ async def read_own_items(
|
|||||||
return [{"Current user name": current_user.name, "Current user surname": current_user.surname}]
|
return [{"Current user name": current_user.name, "Current user surname": current_user.surname}]
|
||||||
|
|
||||||
|
|
||||||
|
# начисляем баллы пользователю
|
||||||
|
@app.post("/api/user/rating")
|
||||||
|
def add_points(data: schemas.AddPoints):
|
||||||
|
user = utils.get_user(data.user_id)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
user.rating += data.rate
|
||||||
|
return {"Success": True}
|
||||||
|
|
||||||
|
|
||||||
|
# получаем данные о баллах пользователя
|
||||||
|
@app.get("/api/user/rating")
|
||||||
|
def add_points(user_id: int):
|
||||||
|
user = utils.get_user(user_id)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
return {"rating": user.rating}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/trashbox", response_model=List[schemas.TrashboxResponse])
|
@app.get("/api/trashbox", response_model=List[schemas.TrashboxResponse])
|
||||||
def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api
|
def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api
|
||||||
BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин
|
BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин
|
||||||
@ -214,3 +238,8 @@ def get_trashboxes(lat:float, lng:float):#крутая функция для р
|
|||||||
@app.get("/{rest_of_path:path}")
|
@app.get("/{rest_of_path:path}")
|
||||||
async def react_app(req: Request, rest_of_path: str):
|
async def react_app(req: Request, rest_of_path: str):
|
||||||
return templates.TemplateResponse('index.html', { 'request': req })
|
return templates.TemplateResponse('index.html', { 'request': req })
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("api/announcement/dispose")
|
||||||
|
def dispose(despose_data: schemas.DisposeData, current_user: Annotated[schemas.User, Depends(utils.get_current_user)]):
|
||||||
|
current_user.rating += 60
|
@ -1,12 +1,17 @@
|
|||||||
from sqlalchemy import Column, Integer, String, Boolean, Float, DateTime, Date, ForeignKey
|
from sqlalchemy import Column, Integer, String, Boolean, Float, DateTime, Date, ForeignKey, Enum, UniqueConstraint
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
from .db import Base, engine
|
from .db import Base, engine
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
|
|
||||||
# class User(SQLAlchemyBaseUserTableUUID, Base):
|
# импортируем модуль для создания перечислений
|
||||||
# name = Column(String, nullable=True)#имя пользователя
|
import enum
|
||||||
|
|
||||||
|
# # класс, необходимый для создания перечисления
|
||||||
|
# class State(enum.Enum):
|
||||||
|
# published = 1
|
||||||
|
# taken = 2
|
||||||
|
# obsolete = 3
|
||||||
|
|
||||||
|
|
||||||
class User(Base):#класс пользователя
|
class User(Base):#класс пользователя
|
||||||
@ -18,30 +23,34 @@ class User(Base):#класс пользователя
|
|||||||
name = Column(String, nullable=True)#имя пользователя
|
name = Column(String, nullable=True)#имя пользователя
|
||||||
surname = Column(String)#фамилия пользователя
|
surname = Column(String)#фамилия пользователя
|
||||||
disabled = Column(Boolean, default=False)
|
disabled = Column(Boolean, default=False)
|
||||||
|
rating = Column(Integer, default=0) #баллы пользователя
|
||||||
|
reg_date = Column(Date) # дата регистрации
|
||||||
|
|
||||||
items = relationship("Announcement", back_populates="owner")
|
items = relationship("Announcement", back_populates="users")
|
||||||
|
|
||||||
class Announcement(Base): #класс объявления
|
class Announcement(Base): #класс объявления
|
||||||
__tablename__ = "announcements"
|
__tablename__ = "announcements"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)#айди объявления
|
id = Column(Integer, primary_key=True, index=True) # айди объявления
|
||||||
owner_id = Column(Integer, ForeignKey("users.id"))#айди создателя объявления
|
user_id = Column(Integer, ForeignKey("users.id", name="fk_users_id")) # айди создателя объявления
|
||||||
name = Column(String) # название объявления
|
name = Column(String) # название объявления
|
||||||
category = Column(String)#категория продукта из объявления
|
category = Column(String) #категория продукта из объявления
|
||||||
best_by = Column(Date)#срок годности продукта из объявления
|
best_by = Column(Date) #срок годности продукта из объявления
|
||||||
address = Column(String)
|
address = Column(String)
|
||||||
longtitude = Column(Integer)
|
longtitude = Column(Integer)
|
||||||
latitude = Column(Integer)
|
latitude = Column(Integer)
|
||||||
description = Column(String)#описание продукта в объявлении
|
description = Column(String) #описание продукта в объявлении
|
||||||
src = Column(String, nullable=True) #изображение продукта в объявлении
|
src = Column(String, nullable=True) #изображение продукта в объявлении
|
||||||
metro = Column(String)#ближайщее метро от адреса нахождения продукта
|
metro = Column(String) #ближайщее метро от адреса нахождения продукта
|
||||||
trashId = Column(Integer, nullable=True)
|
trashId = Column(Integer, nullable=True)
|
||||||
booked_by = Column(Integer)#статус бронирования (либо -1, либо айди бронирующего)
|
booked_by = Column(Integer) #статус бронирования (либо -1, либо айди бронирующего)
|
||||||
|
# state = Column(Enum(State), default=State.published) # состояние объявления (опубликовано, забронировано, устарело)
|
||||||
|
obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным)
|
||||||
|
|
||||||
owner = relationship("User", back_populates="items")
|
owner = relationship("User", back_populates="items")
|
||||||
|
|
||||||
|
|
||||||
class Trashbox(Base):#класс мусорных баков
|
class Trashbox(Base): #класс мусорных баков
|
||||||
__tablename__ = "trashboxes"
|
__tablename__ = "trashboxes"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)#айди
|
id = Column(Integer, primary_key=True, index=True)#айди
|
||||||
@ -49,7 +58,7 @@ class Trashbox(Base):#класс мусорных баков
|
|||||||
address = Column(String)
|
address = Column(String)
|
||||||
latitude = Column(Integer)
|
latitude = Column(Integer)
|
||||||
longtitude = Column(Integer)
|
longtitude = Column(Integer)
|
||||||
category = Column(String)#категория продукта из объявления
|
category = Column(String) #категория продукта из объявления
|
||||||
|
|
||||||
|
|
||||||
class Poems(Base):#класс поэзии
|
class Poems(Base):#класс поэзии
|
||||||
|
@ -71,3 +71,21 @@ class TrashboxRequest(TrashboxBase):
|
|||||||
class DisposeRequest(BaseModel):
|
class DisposeRequest(BaseModel):
|
||||||
ann_id: int
|
ann_id: int
|
||||||
trashbox: TrashboxRequest
|
trashbox: TrashboxRequest
|
||||||
|
|
||||||
|
class SortAnnouncements(BaseModel):
|
||||||
|
expired: Union[int, None] = False
|
||||||
|
user_id: Union[int, None] = None
|
||||||
|
metro: Union[str, None] = None
|
||||||
|
category: Union[str, None] = None
|
||||||
|
# booked_by: Union[int, None] = None
|
||||||
|
|
||||||
|
class DisposeData(BaseModel):
|
||||||
|
ann_id: int # id объявления
|
||||||
|
trash_id: int # id мусорки
|
||||||
|
trash_category: str # категория мусора
|
||||||
|
|
||||||
|
|
||||||
|
# схема для начисления баллов
|
||||||
|
class AddPoints(BaseModel):
|
||||||
|
user_id: int
|
||||||
|
rate: int
|
@ -1,6 +1,9 @@
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from .models import Poems
|
from typing import Annotated, Union
|
||||||
|
from fastapi import Depends
|
||||||
|
from . import models, schemas, utils
|
||||||
import random
|
import random
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
# Загружаем стихи
|
# Загружаем стихи
|
||||||
@ -31,20 +34,34 @@ def add_poems_to_db(db: Session):
|
|||||||
def generate_poem(db: Session):
|
def generate_poem(db: Session):
|
||||||
# генерируем 1 случайное id и выбираем объект бд с этим id
|
# генерируем 1 случайное id и выбираем объект бд с этим id
|
||||||
rand_id = random.randint(1, 102)
|
rand_id = random.randint(1, 102)
|
||||||
poem = db.query(Poems).filter(Poems.id == rand_id).first()
|
poem = db.query(models.Poems).filter(models.Poems.id == rand_id).first()
|
||||||
# возвращаем название и текст стихотворения
|
# возвращаем название и текст стихотворения
|
||||||
return {"name": poem.poem_name, "text": poem.poem_text, "author":""} # добавить поле author в Poems
|
return {"name": poem.poem_name, "text": poem.poem_text, "author":""} # добавить поле author в Poems
|
||||||
|
|
||||||
|
|
||||||
# Функция, создающая сессию БД при каждом запросе к нашему API.
|
def get_query_results(db: Annotated[Session, Depends(utils.get_db)], schema: schemas.SortAnnouncements):
|
||||||
# Срабатывает до запуска остальных функций.
|
"""Функция для последовательного применения различных фильтров (через схему SortAnnouncements)"""
|
||||||
# Всегда закрывает сессию при окончании работы с ней
|
res = db.query(models.Announcement)
|
||||||
# @app.middleware("http")
|
fields = schema.__dict__ # параметры передоваемой схемы SortAnnouncements (ключи и значения)
|
||||||
# async def db_session_middleware(request: Request, call_next):
|
for name, filt in fields.items():
|
||||||
# response = Response("Internal server error", status_code=500)
|
if filt is not None:
|
||||||
# try:
|
d = {name: filt}
|
||||||
# request.state.db = SessionLocal()
|
res = res.filter_by(**d)
|
||||||
# response = await call_next(request)
|
return res.all()
|
||||||
# finally:
|
|
||||||
# request.state.db.close()
|
|
||||||
# return response
|
def check_obsolete(db: Annotated[Session, Depends(utils.get_db)], current_date: datetime.date):
|
||||||
|
"""
|
||||||
|
Возвращаем список объектов базы данных типа Announcement
|
||||||
|
, удовлетворяющих условию obsolete == True
|
||||||
|
"""
|
||||||
|
list_to_return = []
|
||||||
|
not_obsolete = database.query(models.Announcement).filter(models.Announcement.obsolete == True).all()
|
||||||
|
for ann in not_obsolete:
|
||||||
|
if ann.best_by < current_date:
|
||||||
|
ann.obsolete = False
|
||||||
|
else:
|
||||||
|
list_to_return.append(ann)
|
||||||
|
|
||||||
|
return list_to_return
|
||||||
|
|
||||||
|
@ -36,15 +36,15 @@ def get_password_hash(password):
|
|||||||
return pwd_context.hash(password)
|
return pwd_context.hash(password)
|
||||||
|
|
||||||
|
|
||||||
def get_user(db: Session, user_id: int):
|
def get_user(db: Annotated[Session, Depends(get_db)], user_id: int):
|
||||||
user_with_required_id = db.query(models.User).filter(models.User.id == user_id).first()
|
user_with_required_id = db.query(models.User).filter(models.User.id == user_id).first()
|
||||||
if user_with_required_id:
|
if user_with_required_id:
|
||||||
return user_with_required_id
|
return user_with_required_id
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def authenticate_user(db: Session, email: str, password: str):
|
def authenticate_user(db: Annotated[Session, Depends(get_db)], email: str, password: str):
|
||||||
user = get_user(db, user_id)
|
user = get_user(user_id = user_id)
|
||||||
if not user:
|
if not user:
|
||||||
return False
|
return False
|
||||||
if not verify_password(password, user.hashed_password):
|
if not verify_password(password, user.hashed_password):
|
||||||
@ -81,7 +81,6 @@ async def get_current_user(db: Annotated[Session, Depends(get_db)], token: Annot
|
|||||||
if user is None:
|
if user is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
return schemas.User(id=user.id, email=user.email, name=user.name, surname=user.surname, disabled=user.disabled, items=user.items)
|
return schemas.User(id=user.id, email=user.email, name=user.name, surname=user.surname, disabled=user.disabled, items=user.items)
|
||||||
# return user
|
|
||||||
|
|
||||||
|
|
||||||
async def get_current_active_user(
|
async def get_current_active_user(
|
||||||
|
@ -17,12 +17,7 @@ config = context.config
|
|||||||
if config.config_file_name is not None:
|
if config.config_file_name is not None:
|
||||||
fileConfig(config.config_file_name)
|
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 = base.Base.metadata
|
target_metadata = base.Base.metadata
|
||||||
# target_metadata = None
|
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
# can be acquired:
|
# can be acquired:
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
"""points added to user model; owner_id -> user_id; added state(announcement table)
|
||||||
|
|
||||||
|
Revision ID: 4e4d30fd58fc
|
||||||
|
Revises: 00529d20660b
|
||||||
|
Create Date: 2023-08-05 09:38:03.284306
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '4e4d30fd58fc'
|
||||||
|
down_revision = '00529d20660b'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('user_id', sa.Integer(), nullable=True))
|
||||||
|
batch_op.add_column(sa.Column('state', sa.Enum('published', 'taken', 'obsolete', name='state'), nullable=True))
|
||||||
|
# batch_op.drop_constraint(None, type_='foreignkey')
|
||||||
|
batch_op.create_foreign_key('fk_users_id', 'users', ['user_id'], ['id'])
|
||||||
|
batch_op.drop_column('owner_id')
|
||||||
|
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('rating', sa.Integer(), nullable=True))
|
||||||
|
batch_op.add_column(sa.Column('reg_date', sa.Date(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('reg_date')
|
||||||
|
batch_op.drop_column('rating')
|
||||||
|
|
||||||
|
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('owner_id', sa.INTEGER(), nullable=True))
|
||||||
|
# batch_op.drop_constraint('fk_users_id', type_='foreignkey')
|
||||||
|
batch_op.create_foreign_key(None, 'users', ['owner_id'], ['id'])
|
||||||
|
batch_op.drop_column('state')
|
||||||
|
batch_op.drop_column('user_id')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
@ -0,0 +1,34 @@
|
|||||||
|
"""state(enum) -> obsolete(Boolean)
|
||||||
|
|
||||||
|
Revision ID: faecbd04e5eb
|
||||||
|
Revises: 4e4d30fd58fc
|
||||||
|
Create Date: 2023-08-05 23:57:31.784676
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'faecbd04e5eb'
|
||||||
|
down_revision = '4e4d30fd58fc'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('obsolete', sa.Boolean(), nullable=True))
|
||||||
|
batch_op.drop_column('state')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('state', sa.VARCHAR(length=9), nullable=True))
|
||||||
|
batch_op.drop_column('obsolete')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
Loading…
x
Reference in New Issue
Block a user