-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow Migration
table's name and schema to be overridden
#928
base: master
Are you sure you want to change the base?
Conversation
@sinisaos What do you think of this idea? |
Codecov ReportAttention:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #928 +/- ##
==========================================
- Coverage 92.02% 91.97% -0.05%
==========================================
Files 108 109 +1
Lines 8209 8254 +45
==========================================
+ Hits 7554 7592 +38
- Misses 655 662 +7 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great and works as usual :). Another benefit (besides migrations etc.) is that, as you wrote, the user can use this file to provide custom configuration that can be used anywhere in the app as needed, which I think is great.
TL;DR - no Longer explanation: I'm going to be straight-up honest here. The improvements which you have proposed are essential and very welcome, however I must disagree with the entire current concept of how Piccolo globals and config files must be used. Here's what an ideal solution would look like: What should happen instead is that the global state of the library should be managed inside the library itself and the user should just import and call functions from within the library which alter the global database engine configuration, migrations schema name, etc. The less config files a library needs, the more elegant you are able to define its interface and improve Developer Experience. For instance, if it would be implemented correctly, there would be no need for some kind of It must be also kept in mind, that the user most likely might want to build a Docker image from their project, which means that their app is executed from some kind of |
@aabmets The reason we have DB = PostgresEngine(...)
class MyTable(Table, db=DB):
... Which works fine, but the downsides are:
Alternatively, the user could does this: # main.py
for table in (MyTable, SomeOtherTable, ...):
table._meta.db = DB This works fine, as long as the user doesn't miss out any tables. It gets around point 2 above, because you can configure tables which you didn't write yourself. The problem though, is Piccolo has two potential entry points. The first is something like With the Piccolo CLI, it doesn't give the user a chance to configure all of their tables, so the I'm not saying things at the moment are perfect. I can see the value in a very explicit style, and Piccolo will evolve with time. But I hope that gives some context for the existence of the whole I'm not 100% sure the |
@dantownsend EDIT: Also, why do you assume the CLI is a necessary part of Piccolo? When a python app using piccolo for DB is installed into a K8s cluster with a Helm chart, it is necessary to run a DB migration job in the cluster to apply whatever changes to the target database. In this use case, one has to use the underlying migration commands directly with some control logic to create and run migrations: from piccolo.apps.migrations.commands import forwards, backwards, new
async def main():
args = parse_args()
if args.create:
await new.new(
app_name=AppMeta.PICCOLO_APP_NAME,
auto=True
)
elif args.forward:
await forwards.forwards(
app_name=AppMeta.PICCOLO_APP_NAME,
fake=False
)
elif args.backward:
await backwards.backwards(
app_name=AppMeta.PICCOLO_APP_NAME,
migration_id='1',
auto_agree=True
)
elif args.undo_all:
await backwards.backwards(
app_name=AppMeta.PICCOLO_APP_NAME,
migration_id='all',
auto_agree=True
) In the IDE, it is not necessary to use the CLI, because I can just call the migration script directly with whatever option that I specificed for creating a new migration. The script works correctly, because at the top I |
@aabmets That's a good point - at the moment # piccolo_conf.py
DB = PostgresEngine(...)
app = AppConfig(app_name="main", ...)
APP_REGISTRY = AppRegistry(apps=[app]) It just requires a small change here: Lines 231 to 254 in 46e72f4
So if it's an |
@aabmets Sorry to jump into this discussion, but I think it would be best if you make a PR (I hope @dantownsend agrees) on how Piccolo should look and work without these config files and remain easy to use for the average user like me. Thanks in advance. |
@sinisaos |
@aabmets Thank you for your reply and clarification. |
Hey @dantownsend I love your idea (Sorry for jumping here) but I also understand what @aabmets was mentioning above. A good way to do this is to so what Esmerald for instance does with some lazy global settings where you could override the defaults if needed to? Dymmond made it available that well known lazy settings approach that was inspired by Django. Link here maybe I'm missing something but I do believe this could meet what you are looking for without incurring the developer with extra configuration files? @sinisaos I think this would also make your life easier? Again, apologies for jumping here and if I'm not seeing the whole picture. This is an example that works out of the box for basically any project. This also allows the settings to be called anywhere in the app without those annoying partial import errors. That is why it was developed and it's maintained. |
@tarsil Thanks for your suggestion. I use pydantic-settings (I didn't go into details, but it seems to me that |
@sinisaos |
Thanks everyone for their input. One thing we could do is let the user pass in the We do something similar in other places - like Piccolo Admin. The Piccolo CLI commands are nothing special - they're just standard functions. piccolo/piccolo/apps/migrations/commands/check.py Lines 116 to 124 in 46e72f4
We could accept the path to a Migration table, if using it on the command line:
Or if running it from your own script: from piccolo.apps.migrations.table import Migration as MigrationBase
class Migration(MigrationBase, schema="my_schema"):
...
await check_migrations(migration_table=Migration) |
from __future__ import annotations
from typing import Type
from functools import lru_cache
from piccolo.table import Table
@lru_cache
def migration_table_factory(table_name: str, schema_name: str) -> Type[Table]:
class _CustomMigration(Table, tablename=table_name, schema=schema_name):
# class content is identical to the original Migration table
return _CustomMigration This solution would require replacing all direct access of the Migration table with a call to the |
Related discussion #895
Piccolo has some important tables which are provided out of the box - most importantly
Migration
for tracking database migrations, andBaseUser
for user management.The name and Postgres schema of these tables is currently fixed. The problem is:
The solution up until now has required hacks, or copying the Piccolo apps into your own codebase and modifying them.
I've been through a few potential solutions. The most obvious is adding configuration options in
piccolo_conf.py
. The problem though is whenpiccolo_conf.py
is loaded, it runs some validation on your project to make sure all of the registered apps and tables are available. So if our tables rely on configuration inpiccolo_conf.py
to be created, it would create a circular dependency.This PR lets the user create a
piccolo_overrides.toml
file at the root of the their project (alongside thepiccolo_conf.py
file, where the Python interpreter will be launched). For example:Because it's a static file, it has some benefits:
Users can use this file to provide configuration for their own custom apps too, if they want.
What do people think?