Skip to content
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

feat: create database models #6

Merged
merged 3 commits into from Dec 20, 2023
Merged

Conversation

greyli
Copy link
Contributor

@greyli greyli commented Dec 18, 2023

  • Add db models
  • Add commands to create/drop tables
  • Fix pdm serve command

@greyli greyli changed the title Create database models feat: create database models Dec 18, 2023
@yuxiaoy1
Copy link
Contributor

2 questions:

  • why not following SQLAlchemy 2.0 code style?
  • commands can be a flask Blueprint and use app.register_blueprint(commands) directly, so no reigster_commands needed. and I suggest to rename directory views to blueprints for better understanding, like from app.blueprints.commands import commands.

@greyli
Copy link
Contributor Author

greyli commented Dec 18, 2023

  • why not following SQLAlchemy 2.0 code style?

Good suggestion! I have to admit I have never writen the 2.0 style model class. I will update it later. Or maybe we could merge this first since it blocks others tasks.

  • commands can be a flask Blueprint and use app.register_blueprint(commands) directly, so no reigster_commands needed. and I suggest to rename directory views to blueprints for better understanding, like from app.blueprints.commands import commands.

I was try to keep the "views" folder for "view functions", other configuration for the app goes to the "core" folder.

@frostming What do you think?

@yuxiaoy1
Copy link
Contributor

The module main, admin are all blueprints, while you can add views in that module. In larger applicaiton, you can optionally structure like this: blueprints/main/__init__.py, blueprints/main/views.py( or .../routes.py), which the naming convention would be more accurate.

.flaskenv Outdated
@@ -1,2 +0,0 @@
FLASK_DEBUG=1
FLASK_APP=backend/app.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't notice it is pointing at backend subfolder, then I prefer to keep it at the top level but fixing the tool.pdm.scripts instead.

@frostming
Copy link
Contributor

  • commands can be a flask Blueprint and use app.register_blueprint(commands) directly

I am confused by this, how can commands be a blueprint but it isn't?

The module main, admin are all blueprints, while you can add views in that module. In larger applicaiton, you can optionally structure like this: blueprints/main/__init__.py, blueprints/main/views.py( or .../routes.py), which the naming convention would be more accurate.

I agree with this restructure.

@yuxiaoy1
Copy link
Contributor

You can create a commands blueprint and set the cli_group argument to None, then you are free to add commands with @commands.cli.command as usual.

@frostming
Copy link
Contributor

frostming commented Dec 19, 2023

You can create a commands blueprint and set the cli_group argument to None, then you are free to add commands with @commands.cli.command as usual.

I see, but the naming is not right. They are app-level commands that do not belong to any blueprint.

@yuxiaoy1
Copy link
Contributor

It's app level, the key point is cli_group argument, so you can use flask xxx, otherwise you need to use flask commands xxx.

@zhaoblake
Copy link
Contributor

Any updates on this PR? I am planning to build APIs, but I am blocked because of this. @frostming @greyli

backend/bamboo/models.py Outdated Show resolved Hide resolved
backend/bamboo/models.py Outdated Show resolved Hide resolved
backend/bamboo/models.py Outdated Show resolved Hide resolved
- Add db models
- Add commands to create/drop tables
- Fix pdm serve command
@greyli
Copy link
Contributor Author

greyli commented Dec 19, 2023

Fixed all issues and migrated to SQLAlchemy 2.x code style. @frostming @yuxiaoy1 Please help to review.

backend/bamboo/models.py Show resolved Hide resolved


class User(db.Model):
id: Mapped[int] = mapped_column(Integer, primary_key=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQLAlchemy 2.x automatically set the data type with type hint, so the declaration can be simplified to id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] username: Mapped[str] ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

password_hash: Mapped[str] = mapped_column(String)
biology: Mapped[str] = mapped_column(String)
introduction: Mapped[str] = mapped_column(String)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to https://blog.miguelgrinberg.com/post/it-s-time-for-a-change-datetime-utcnow-is-now-deprecated, it's better to use default=lambda: datetime.now(timezone.utc)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated and used datetime.UTC to replace timezone.utc.

introduction: Mapped[str] = mapped_column(String)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
active: Mapped[str] = mapped_column(Boolean)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be Mapped[bool], and remove the mapped_column

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

from sqlalchemy.orm import DeclarativeBase


class Base(DeclarativeBase):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's recommended by the Flask-SQLAlchemy docs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but I have checked there is no difference w/ or w/o it, I think the docs need get updates.

role_id: Mapped[int] = mapped_column(Integer, ForeignKey('role.id'))

profile_image = relationship('Media', foreign_keys=[profile_image_id])
role = relationship('Role', back_populates='users')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use WriteOnlyMapped for relationship: role: WriteOnlyMapped['Role'] = relationship(back_populates='users')
All relationship could be updated as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

name: Mapped[str] = mapped_column(String)
username: Mapped[str] = mapped_column(String)
password_hash: Mapped[str] = mapped_column(String)
biology: Mapped[str] = mapped_column(String)
Copy link
Contributor

@yuxiaoy1 yuxiaoy1 Dec 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be biography? or just bio.
And this could be null so add optional type hint: bio: Mapped[Optional[str]], or it would get not null constraint error using sqlite.
All field optional need updates as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to bio and updated optional fields.

@greyli
Copy link
Contributor Author

greyli commented Dec 19, 2023

@yuxiaoy1 Thank you for the thorough review. I have updated the code based on your suggestions.

@greyli
Copy link
Contributor Author

greyli commented Dec 19, 2023

The new code throws:

➜  bamboo git:(db-models) pdm create-tables
Traceback (most recent call last):
  File "/home/greyli/bamboo/.venv/bin/flask", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/flask/cli.py", line 1064, in main
    cli.main()
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/click/core.py", line 1682, in invoke
    cmd_name, cmd, args = self.resolve_command(ctx, args)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/click/core.py", line 1729, in resolve_command
    cmd = self.get_command(ctx, cmd_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/flask/cli.py", line 579, in get_command
    app = info.load_app()
          ^^^^^^^^^^^^^^^
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/flask/cli.py", line 309, in load_app
    app = locate_app(import_name, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/greyli/bamboo/.venv/lib/python3.12/site-packages/flask/cli.py", line 219, in locate_app
    __import__(module_name)
  File "/home/greyli/bamboo/backend/app.py", line 8, in <module>
    from bamboo import create_app  # noqa
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/greyli/bamboo/backend/bamboo/__init__.py", line 9, in <module>
    from bamboo.core.commands import register_commands
  File "/home/greyli/bamboo/backend/bamboo/core/commands.py", line 4, in <module>
    from bamboo.models import *
  File "/home/greyli/bamboo/backend/bamboo/models.py", line 12, in <module>
    class User(db.Model):
  File "/home/greyli/bamboo/backend/bamboo/models.py", line 25, in User
    profile_image = WriteOnlyMapped['Media'] = relationship(foreign_keys=[profile_image_id])
                    ~~~~~~~~~~~~~~~^^^^^^^^^
TypeError: 'type' object does not support item assignment

I think it's better to move the migration to a separate pull request. Removed the last commit.

@yuxiaoy1
Copy link
Contributor

Ok I think we can pull request later.

class Site(db.Model):
__tablename__ = 'site'
id = Column(Integer, primary_key=True)
config = Column(Text)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does sqlite3 support JSON natively? It seems so for the latest versions. And both MySQL and PostgreSQL support JSON. Maybe we can update it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. SQLite 3.9 and later supports the JSON field.

Copy link
Contributor

@frostming frostming left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, just a minor comment and I think we should merge this one to unblock the other tasks.

@frostming frostming merged commit c46ce08 into bamboo-cms:main Dec 20, 2023
2 checks passed
@greyli greyli deleted the db-models branch December 20, 2023 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants