Decouple blog: use shared.models for all cross-app imports
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 47s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 47s
- Replace all imports from cart.models, market.models, events.models with shared.models equivalents - Convert blog/models/ghost_content.py to re-export stub - Update shared + glue submodule pointers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ from sqlalchemy.orm.attributes import flag_modified # for non-Mutable JSON colu
|
||||
from models.ghost_content import (
|
||||
Post, Author, Tag, PostAuthor, PostTag
|
||||
)
|
||||
from cart.models.page_config import PageConfig
|
||||
from shared.models.page_config import PageConfig
|
||||
|
||||
# User-centric membership models
|
||||
from shared.models import User
|
||||
|
||||
@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload, joinedload
|
||||
|
||||
from models.ghost_content import Post, Author, Tag, PostTag
|
||||
from cart.models.page_config import PageConfig
|
||||
from shared.models.page_config import PageConfig
|
||||
from models.tag_group import TagGroup, TagGroupTag
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from ..ghost_db import DBClient # adjust import path
|
||||
from sqlalchemy import select
|
||||
from models.ghost_content import PostLike
|
||||
from events.models.calendars import CalendarEntry, CalendarEntryPost
|
||||
from shared.models.calendars import CalendarEntry, CalendarEntryPost
|
||||
from quart import g
|
||||
|
||||
async def posts_data(
|
||||
|
||||
@@ -22,7 +22,7 @@ def register():
|
||||
@require_admin
|
||||
async def admin(slug: str):
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from cart.models.page_config import PageConfig
|
||||
from shared.models.page_config import PageConfig
|
||||
from sqlalchemy import select as sa_select
|
||||
|
||||
# Load features for page admin
|
||||
@@ -62,7 +62,7 @@ def register():
|
||||
@require_admin
|
||||
async def update_features(slug: str):
|
||||
"""Update PageConfig.features for a page."""
|
||||
from cart.models.page_config import PageConfig
|
||||
from shared.models.page_config import PageConfig
|
||||
from models.ghost_content import Post
|
||||
from sqlalchemy import select as sa_select
|
||||
from quart import jsonify
|
||||
@@ -129,7 +129,7 @@ def register():
|
||||
@require_admin
|
||||
async def update_sumup(slug: str):
|
||||
"""Update PageConfig SumUp credentials for a page."""
|
||||
from cart.models.page_config import PageConfig
|
||||
from shared.models.page_config import PageConfig
|
||||
from sqlalchemy import select as sa_select
|
||||
from quart import jsonify
|
||||
|
||||
@@ -191,7 +191,7 @@ def register():
|
||||
@require_admin
|
||||
async def calendar_view(slug: str, calendar_id: int):
|
||||
"""Show calendar month view for browsing entries"""
|
||||
from events.models.calendars import Calendar
|
||||
from shared.models.calendars import Calendar
|
||||
from sqlalchemy import select
|
||||
from datetime import datetime, timezone
|
||||
from quart import request
|
||||
@@ -273,7 +273,7 @@ def register():
|
||||
@require_admin
|
||||
async def entries(slug: str):
|
||||
from ..services.entry_associations import get_post_entry_ids
|
||||
from events.models.calendars import Calendar
|
||||
from shared.models.calendars import Calendar
|
||||
from sqlalchemy import select
|
||||
|
||||
post_id = g.post_data["post"]["id"]
|
||||
@@ -309,7 +309,7 @@ def register():
|
||||
@require_admin
|
||||
async def toggle_entry(slug: str, entry_id: int):
|
||||
from ..services.entry_associations import toggle_entry_association, get_post_entry_ids, get_associated_entries
|
||||
from events.models.calendars import Calendar
|
||||
from shared.models.calendars import Calendar
|
||||
from sqlalchemy import select
|
||||
from quart import jsonify
|
||||
|
||||
@@ -603,7 +603,7 @@ def register():
|
||||
@require_admin
|
||||
async def markets(slug: str):
|
||||
"""List markets for this page."""
|
||||
from market.models.market_place import MarketPlace
|
||||
from shared.models.market_place import MarketPlace
|
||||
from sqlalchemy import select as sa_select
|
||||
|
||||
post = (g.post_data or {}).get("post", {})
|
||||
@@ -631,7 +631,7 @@ def register():
|
||||
async def create_market(slug: str):
|
||||
"""Create a new market for this page."""
|
||||
from ..services.markets import create_market as _create_market, MarketError
|
||||
from market.models.market_place import MarketPlace
|
||||
from shared.models.market_place import MarketPlace
|
||||
from sqlalchemy import select as sa_select
|
||||
from quart import jsonify
|
||||
|
||||
@@ -669,7 +669,7 @@ def register():
|
||||
async def delete_market(slug: str, market_slug: str):
|
||||
"""Soft-delete a market."""
|
||||
from ..services.markets import soft_delete_market
|
||||
from market.models.market_place import MarketPlace
|
||||
from shared.models.market_place import MarketPlace
|
||||
from sqlalchemy import select as sa_select
|
||||
from quart import jsonify
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ from quart import (
|
||||
)
|
||||
from .services.post_data import post_data
|
||||
from .services.post_operations import toggle_post_like
|
||||
from events.models.calendars import Calendar
|
||||
from market.models.market_place import MarketPlace
|
||||
from shared.models.calendars import Calendar
|
||||
from shared.models.market_place import MarketPlace
|
||||
from sqlalchemy import select
|
||||
|
||||
from shared.browser.app.redis_cacher import cache_page, clear_cache
|
||||
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from events.models.calendars import CalendarEntry, CalendarEntryPost, Calendar
|
||||
from shared.models.calendars import CalendarEntry, CalendarEntryPost, Calendar
|
||||
from models.ghost_content import Post
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import unicodedata
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from market.models.market_place import MarketPlace
|
||||
from shared.models.market_place import MarketPlace
|
||||
from models.ghost_content import Post
|
||||
from cart.models.page_config import PageConfig
|
||||
from shared.models.page_config import PageConfig
|
||||
from shared.browser.app.utils import utcnow
|
||||
from glue.services.relationships import attach_child, detach_child
|
||||
|
||||
|
||||
2
glue
2
glue
Submodule glue updated: fc14d8323a...ebce44e9d9
@@ -1,216 +1,3 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy import (
|
||||
Integer,
|
||||
String,
|
||||
Text,
|
||||
Boolean,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
Column,
|
||||
func,
|
||||
from shared.models.ghost_content import ( # noqa: F401
|
||||
Tag, Post, Author, PostAuthor, PostTag, PostLike,
|
||||
)
|
||||
from shared.db.base import Base # whatever your Base is
|
||||
# from .author import Author # make sure imports resolve
|
||||
# from ..app.blog.calendars.model import Calendar
|
||||
|
||||
class Tag(Base):
|
||||
__tablename__ = "tags"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
ghost_id: Mapped[str] = mapped_column(String(64), index=True, unique=True, nullable=False)
|
||||
|
||||
slug: Mapped[str] = mapped_column(String(191), index=True, nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
|
||||
description: Mapped[Optional[str]] = mapped_column(Text())
|
||||
visibility: Mapped[str] = mapped_column(String(32), default="public", nullable=False)
|
||||
feature_image: Mapped[Optional[str]] = mapped_column(Text())
|
||||
|
||||
meta_title: Mapped[Optional[str]] = mapped_column(String(300))
|
||||
meta_description: Mapped[Optional[str]] = mapped_column(Text())
|
||||
|
||||
created_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
# NEW: posts relationship is now direct Post objects via PostTag
|
||||
posts: Mapped[List["Post"]] = relationship(
|
||||
"Post",
|
||||
secondary="post_tags",
|
||||
primaryjoin="Tag.id==post_tags.c.tag_id",
|
||||
secondaryjoin="Post.id==post_tags.c.post_id",
|
||||
back_populates="tags",
|
||||
order_by="PostTag.sort_order",
|
||||
)
|
||||
|
||||
|
||||
class Post(Base):
|
||||
__tablename__ = "posts"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
ghost_id: Mapped[str] = mapped_column(String(64), index=True, unique=True, nullable=False)
|
||||
uuid: Mapped[str] = mapped_column(String(64), unique=True, nullable=False)
|
||||
slug: Mapped[str] = mapped_column(String(191), index=True, nullable=False)
|
||||
|
||||
title: Mapped[str] = mapped_column(String(500), nullable=False)
|
||||
|
||||
html: Mapped[Optional[str]] = mapped_column(Text())
|
||||
plaintext: Mapped[Optional[str]] = mapped_column(Text())
|
||||
mobiledoc: Mapped[Optional[str]] = mapped_column(Text())
|
||||
lexical: Mapped[Optional[str]] = mapped_column(Text())
|
||||
|
||||
feature_image: Mapped[Optional[str]] = mapped_column(Text())
|
||||
feature_image_alt: Mapped[Optional[str]] = mapped_column(Text())
|
||||
feature_image_caption: Mapped[Optional[str]] = mapped_column(Text())
|
||||
|
||||
excerpt: Mapped[Optional[str]] = mapped_column(Text())
|
||||
custom_excerpt: Mapped[Optional[str]] = mapped_column(Text())
|
||||
|
||||
visibility: Mapped[str] = mapped_column(String(32), default="public", nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(32), default="draft", nullable=False)
|
||||
featured: Mapped[bool] = mapped_column(Boolean(), default=False, nullable=False)
|
||||
is_page: Mapped[bool] = mapped_column(Boolean(), default=False, nullable=False)
|
||||
email_only: Mapped[bool] = mapped_column(Boolean(), default=False, nullable=False)
|
||||
|
||||
canonical_url: Mapped[Optional[str]] = mapped_column(Text())
|
||||
meta_title: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
meta_description: Mapped[Optional[str]] = mapped_column(Text())
|
||||
og_image: Mapped[Optional[str]] = mapped_column(Text())
|
||||
og_title: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
og_description: Mapped[Optional[str]] = mapped_column(Text())
|
||||
twitter_image: Mapped[Optional[str]] = mapped_column(Text())
|
||||
twitter_title: Mapped[Optional[str]] = mapped_column(String(500))
|
||||
twitter_description: Mapped[Optional[str]] = mapped_column(Text())
|
||||
custom_template: Mapped[Optional[str]] = mapped_column(String(191))
|
||||
|
||||
reading_time: Mapped[Optional[int]] = mapped_column(Integer())
|
||||
comment_id: Mapped[Optional[str]] = mapped_column(String(191))
|
||||
|
||||
published_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
created_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
user_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, ForeignKey("users.id", ondelete="SET NULL"), index=True
|
||||
)
|
||||
publish_requested: Mapped[bool] = mapped_column(Boolean(), default=False, server_default="false", nullable=False)
|
||||
|
||||
primary_author_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, ForeignKey("authors.id", ondelete="SET NULL")
|
||||
)
|
||||
primary_tag_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, ForeignKey("tags.id", ondelete="SET NULL")
|
||||
)
|
||||
|
||||
primary_author: Mapped[Optional["Author"]] = relationship(
|
||||
"Author", foreign_keys=[primary_author_id]
|
||||
)
|
||||
primary_tag: Mapped[Optional[Tag]] = relationship(
|
||||
"Tag", foreign_keys=[primary_tag_id]
|
||||
)
|
||||
user: Mapped[Optional["User"]] = relationship(
|
||||
"User", foreign_keys=[user_id]
|
||||
)
|
||||
|
||||
# AUTHORS RELATIONSHIP (many-to-many via post_authors)
|
||||
authors: Mapped[List["Author"]] = relationship(
|
||||
"Author",
|
||||
secondary="post_authors",
|
||||
primaryjoin="Post.id==post_authors.c.post_id",
|
||||
secondaryjoin="Author.id==post_authors.c.author_id",
|
||||
back_populates="posts",
|
||||
order_by="PostAuthor.sort_order",
|
||||
)
|
||||
|
||||
# TAGS RELATIONSHIP (many-to-many via post_tags)
|
||||
tags: Mapped[List[Tag]] = relationship(
|
||||
"Tag",
|
||||
secondary="post_tags",
|
||||
primaryjoin="Post.id==post_tags.c.post_id",
|
||||
secondaryjoin="Tag.id==post_tags.c.tag_id",
|
||||
back_populates="posts",
|
||||
order_by="PostTag.sort_order",
|
||||
)
|
||||
likes: Mapped[List["PostLike"]] = relationship(
|
||||
"PostLike",
|
||||
back_populates="post",
|
||||
cascade="all, delete-orphan",
|
||||
passive_deletes=True,
|
||||
)
|
||||
|
||||
class Author(Base):
|
||||
__tablename__ = "authors"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
ghost_id: Mapped[str] = mapped_column(String(64), index=True, unique=True, nullable=False)
|
||||
|
||||
slug: Mapped[str] = mapped_column(String(191), index=True, nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
email: Mapped[Optional[str]] = mapped_column(String(255))
|
||||
|
||||
profile_image: Mapped[Optional[str]] = mapped_column(Text())
|
||||
cover_image: Mapped[Optional[str]] = mapped_column(Text())
|
||||
bio: Mapped[Optional[str]] = mapped_column(Text())
|
||||
website: Mapped[Optional[str]] = mapped_column(Text())
|
||||
location: Mapped[Optional[str]] = mapped_column(Text())
|
||||
facebook: Mapped[Optional[str]] = mapped_column(Text())
|
||||
twitter: Mapped[Optional[str]] = mapped_column(Text())
|
||||
|
||||
created_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
# backref to posts via post_authors
|
||||
posts: Mapped[List[Post]] = relationship(
|
||||
"Post",
|
||||
secondary="post_authors",
|
||||
primaryjoin="Author.id==post_authors.c.author_id",
|
||||
secondaryjoin="Post.id==post_authors.c.post_id",
|
||||
back_populates="authors",
|
||||
order_by="PostAuthor.sort_order",
|
||||
)
|
||||
|
||||
class PostAuthor(Base):
|
||||
__tablename__ = "post_authors"
|
||||
|
||||
post_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("posts.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
)
|
||||
author_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("authors.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
)
|
||||
sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
|
||||
|
||||
class PostTag(Base):
|
||||
__tablename__ = "post_tags"
|
||||
|
||||
post_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("posts.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
)
|
||||
tag_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tags.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
)
|
||||
sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
|
||||
|
||||
class PostLike(Base):
|
||||
__tablename__ = "post_likes"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
post_id: Mapped[int] = mapped_column(ForeignKey("posts.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
post: Mapped["Post"] = relationship("Post", back_populates="likes", foreign_keys=[post_id])
|
||||
user = relationship("User", back_populates="liked_posts")
|
||||
|
||||
2
shared
2
shared
Submodule shared updated: da10fc4cf9...0c0f3c8416
Reference in New Issue
Block a user