diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..7c6635f --- /dev/null +++ b/alembic.ini @@ -0,0 +1,85 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat migrations/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///assets/test.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks=black +# black.type=console_scripts +# black.entrypoint=black +# black.options=-l 79 + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/database/models.py b/database/models.py index 289d657..e0ed92a 100644 --- a/database/models.py +++ b/database/models.py @@ -71,14 +71,12 @@ class Cities(Base): image = Column(String(255)) status = Column(Enum("1", "0")) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) - def __init__(self, name, image, status, created, modified): + def __init__(self, name, image, status): self.name = name self.image = image self.status = status - self.created = created - self.modified = modified class Games(Base): @@ -108,7 +106,7 @@ class Games(Base): noti_status = Column(Integer, server_default=text("0")) conduct_status = Column(Integer, server_default=text("1")) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, @@ -161,7 +159,7 @@ class Payments(Base): pay_mode = Column(Integer) status = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, @@ -198,7 +196,7 @@ class PlayerAvailabilities(Base): player_id = Column(Integer) status = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, game_id, player_id, status, @@ -215,7 +213,7 @@ class PlayerCancelGames(Base): player_id = Column(Integer) game_id = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, player_id, game_id, @@ -232,7 +230,7 @@ class PurchaseGames(Base): user_id = Column(Integer) pay_mode = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, game_id, user_id, pay_mode, @@ -250,7 +248,7 @@ class Sports(Base): spanish_name = Column(String(100)) status = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, name, spanish_name, status, @@ -269,7 +267,7 @@ class Teams(Base): game_id = Column(Integer) status = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, game_id, user_id, team_id, status, @@ -289,7 +287,7 @@ class UserRatings(Base): player_id = Column(Integer) rating = Column(String(100)) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) user_type = Column(Integer) def __init__(self, game_id, user_id, player_id, rating, user_type): @@ -308,7 +306,7 @@ class VenueImages(Base): user_id = Column(Integer) image = Column(String(255)) created = Column(DateTime, nullable=False, server_default=func.now()) - updated = Column(DateTime, nullable=False, onupdate=func.now()) + updated = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, venue_id, user_id, image, @@ -329,7 +327,7 @@ class Venues(Base): name = Column(String(100)) sports_id = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - modified = Column(DateTime, nullable=False, onupdate=func.now()) + modified = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, user_id, address, latitude, longitude, name, sports_id, @@ -349,7 +347,7 @@ class ViewNews(Base): news_id = Column(Integer) user_id = Column(Integer) created = Column(DateTime, nullable=False, server_default=func.now()) - updated = Column(DateTime, nullable=False, onupdate=func.now()) + updated = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, news_id, user_id, @@ -369,7 +367,7 @@ class WebBookings(Base): game = Column(String(255)) city = Column(String(100)) created = Column(DateTime, nullable=False, server_default=func.now()) - updated = Column(DateTime, nullable=False, onupdate=func.now()) + updated = Column(DateTime, nullable=True, onupdate=func.now()) def __init__( self, user_id, address, name, email, contact, message, game, city, diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000..d79bf86 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,77 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +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 = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata, + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +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)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/eaa992d1469b_make_updated_field_nullable.py b/migrations/versions/eaa992d1469b_make_updated_field_nullable.py new file mode 100644 index 0000000..be3ecdd --- /dev/null +++ b/migrations/versions/eaa992d1469b_make_updated_field_nullable.py @@ -0,0 +1,45 @@ +"""make updated field nullable + +Revision ID: eaa992d1469b +Revises: +Create Date: 2020-07-04 18:08:31.035209 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "eaa992d1469b" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + tables = { + "modified": "cities", + "modified": "games", + "cancel_date": "games", + "modified": "payments", + "modified": "player_availabilities", + "modified": "player_cancel_games", + "modified": "purchase_games", + "modified": "sports", + "modified": "teams", + "modified": "user_ratings", + "modified": "venues", + "updated": "venue_images", + "updated": "web_bookings", + } + for field, table in tables.items(): + with op.batch_alter_table(table) as batch_op: + batch_op.alter_column(column_name=field, nullable=True) + query = "UPDATE {0} SET {1} = NULL WHERE {1} = '0000-00-00 00:00:00'".format( + table, field + ) + op.execute(query) + + +def downgrade(): + pass diff --git a/tests/queries_test.py b/tests/queries_test.py index a124804..309c9ba 100644 --- a/tests/queries_test.py +++ b/tests/queries_test.py @@ -18,7 +18,7 @@ def test_cities(): def test_games(): test = db.query(Games).filter(Games.id == 508).first() - assert test.user_id == 1987 + assert test.user_id == 1978 def test_payments():