Improve the publication table / data types to denormalize the values
Returning profile values where necessary will remove the need to compute heavy joins unless the user actually wants all the data. Now Publication points at OrderProfiles which don't expose the sequences Orders->Sequence->EntryProfile avoids including the full `text` for the sequence.
This commit is contained in:
parent
63e51fa017
commit
86b0d88874
4 changed files with 82 additions and 11 deletions
|
|
@ -1,16 +1,25 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from sqlalchemy import String, Text, ForeignKey, Engine, Interval, UniqueConstraint
|
from sqlalchemy import String, Text, ForeignKey, Engine, Interval, UniqueConstraint
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship, Session
|
from sqlalchemy.orm import Mapped, mapped_column, relationship, Session, joinedload
|
||||||
|
|
||||||
from src.infra.db import Base
|
from src.infra.db import Base
|
||||||
from src.services.publications import Publication, Order, Entry, Sequence, PublicationProfile, PublicationRepo
|
from src.services.publications import (
|
||||||
|
Publication,
|
||||||
|
Order,
|
||||||
|
Entry,
|
||||||
|
Sequence,
|
||||||
|
PublicationProfile,
|
||||||
|
PublicationRepo,
|
||||||
|
OrderProfile,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PublicationEntryModel(Base):
|
class PublicationEntryModel(Base):
|
||||||
__tablename__ = 'publication_entry'
|
__tablename__ = 'publication_entry'
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
title: Mapped[str] = mapped_column(String(256))
|
||||||
text: Mapped[str] = mapped_column(Text())
|
text: Mapped[str] = mapped_column(Text())
|
||||||
|
|
||||||
sequences: Mapped[list['PublicationSequenceModel']] = relationship(back_populates='entry')
|
sequences: Mapped[list['PublicationSequenceModel']] = relationship(back_populates='entry')
|
||||||
|
|
@ -55,13 +64,13 @@ class PublicationModel(Base):
|
||||||
|
|
||||||
|
|
||||||
def model_to_entry(db_pub_entry: PublicationEntryModel) -> Entry:
|
def model_to_entry(db_pub_entry: PublicationEntryModel) -> Entry:
|
||||||
return Entry(id=db_pub_entry.id, text=db_pub_entry.text)
|
return Entry(id=db_pub_entry.id, text=db_pub_entry.text, title=db_pub_entry.title)
|
||||||
|
|
||||||
|
|
||||||
def model_to_sequence(db_pub_position: PublicationSequenceModel) -> Sequence:
|
def model_to_sequence(db_pub_position: PublicationSequenceModel) -> Sequence:
|
||||||
return Sequence(
|
return Sequence(
|
||||||
position=db_pub_position.position,
|
position=db_pub_position.position,
|
||||||
entry=model_to_entry(db_pub_position.entry),
|
entry=model_to_entry(db_pub_position.entry).to_profile(),
|
||||||
duration=db_pub_position.duration,
|
duration=db_pub_position.duration,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -74,13 +83,20 @@ def model_to_order(db_pub_order: PublicationOrderModel) -> Order:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def model_to_order_profile(db_pub_order: PublicationOrderModel) -> OrderProfile:
|
||||||
|
return OrderProfile(
|
||||||
|
id=db_pub_order.id,
|
||||||
|
title=db_pub_order.title,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def model_to_publication(db_pub: PublicationModel) -> Publication:
|
def model_to_publication(db_pub: PublicationModel) -> Publication:
|
||||||
return Publication(
|
return Publication(
|
||||||
id=db_pub.id,
|
id=db_pub.id,
|
||||||
title=db_pub.title,
|
title=db_pub.title,
|
||||||
description=db_pub.description,
|
description=db_pub.description,
|
||||||
by_line=db_pub.by_line,
|
by_line=db_pub.by_line,
|
||||||
orders=list(map(model_to_order, db_pub.orders)),
|
orders=list(map(lambda m: model_to_order_profile(m), db_pub.orders)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -96,7 +112,7 @@ class PublicationRepoImpl(PublicationRepo):
|
||||||
|
|
||||||
def get_publication_by_id(self, pub_id) -> Publication | None:
|
def get_publication_by_id(self, pub_id) -> Publication | None:
|
||||||
with Session(self._db) as session:
|
with Session(self._db) as session:
|
||||||
publication = session.get(PublicationModel, pub_id)
|
publication = session.get(PublicationModel, pub_id, options=[joinedload(PublicationModel.orders)])
|
||||||
|
|
||||||
if publication:
|
if publication:
|
||||||
return model_to_publication(publication)
|
return model_to_publication(publication)
|
||||||
|
|
@ -111,3 +127,22 @@ class PublicationRepoImpl(PublicationRepo):
|
||||||
def get_all_publications(self) -> list[PublicationProfile]:
|
def get_all_publications(self) -> list[PublicationProfile]:
|
||||||
with Session(self._db) as session:
|
with Session(self._db) as session:
|
||||||
return list(map(model_to_publication_profile, session.query(PublicationModel).all()))
|
return list(map(model_to_publication_profile, session.query(PublicationModel).all()))
|
||||||
|
|
||||||
|
def get_full_order(self, order_id: int) -> Order | None:
|
||||||
|
with Session(self._db) as session:
|
||||||
|
order = session.get(
|
||||||
|
PublicationOrderModel, order_id, options=[joinedload(PublicationOrderModel.sequence_entries)]
|
||||||
|
)
|
||||||
|
|
||||||
|
if order:
|
||||||
|
return model_to_order(order)
|
||||||
|
|
||||||
|
def get_full_entry(self, entry_id: int) -> Entry | None:
|
||||||
|
with Session(self._db) as session:
|
||||||
|
order = session.get(
|
||||||
|
PublicationEntryModel,
|
||||||
|
entry_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if order:
|
||||||
|
return model_to_entry(order)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
from .data import Publication, PublicationProfile, Order, Sequence, Entry
|
from .data import Publication, PublicationProfile, Order, Sequence, Entry, OrderProfile
|
||||||
from .service import PublicationService
|
from .service import PublicationService
|
||||||
from .repo import PublicationRepo
|
from .repo import PublicationRepo
|
||||||
|
|
||||||
__all__ = ['Publication', 'PublicationProfile', 'Order', 'Sequence', 'Entry', 'PublicationService', 'PublicationRepo']
|
__all__ = [
|
||||||
|
'Publication',
|
||||||
|
'PublicationProfile',
|
||||||
|
'Order',
|
||||||
|
'OrderProfile',
|
||||||
|
'Sequence',
|
||||||
|
'Entry',
|
||||||
|
'PublicationService',
|
||||||
|
'PublicationRepo',
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,25 +2,44 @@ from datetime import timedelta
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EntryProfile:
|
||||||
|
id: int
|
||||||
|
title: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Entry:
|
class Entry:
|
||||||
id: int
|
id: int
|
||||||
|
title: str
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
|
def to_profile(self) -> EntryProfile:
|
||||||
|
return EntryProfile(self.id, self.title)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Sequence:
|
class Sequence:
|
||||||
position: int
|
position: int
|
||||||
entry: Entry
|
entry: EntryProfile
|
||||||
duration: timedelta | None
|
duration: timedelta | None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OrderProfile:
|
||||||
|
id: int
|
||||||
|
title: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Order:
|
class Order:
|
||||||
id: int
|
id: int
|
||||||
title: str
|
title: str
|
||||||
sequence_entries: list[Sequence]
|
sequence_entries: list[Sequence]
|
||||||
|
|
||||||
|
def to_profile(self) -> OrderProfile:
|
||||||
|
return OrderProfile(id=self.id, title=self.title)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PublicationProfile:
|
class PublicationProfile:
|
||||||
|
|
@ -35,7 +54,7 @@ class Publication:
|
||||||
title: str
|
title: str
|
||||||
description: str
|
description: str
|
||||||
by_line: str
|
by_line: str
|
||||||
orders: list[Order] = field(default_factory=list)
|
orders: list[OrderProfile] = field(default_factory=list)
|
||||||
|
|
||||||
# Rather than using a subclass I think this makes sense
|
# Rather than using a subclass I think this makes sense
|
||||||
def to_profile(self) -> PublicationProfile:
|
def to_profile(self) -> PublicationProfile:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
from .data import Publication, PublicationProfile
|
from .data import Publication, PublicationProfile, Order, Entry
|
||||||
|
|
||||||
|
|
||||||
class PublicationRepo(abc.ABC):
|
class PublicationRepo(abc.ABC):
|
||||||
|
|
@ -15,3 +15,11 @@ class PublicationRepo(abc.ABC):
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_all_publications(self) -> list[PublicationProfile]:
|
def get_all_publications(self) -> list[PublicationProfile]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_full_order(self, order_id: int) -> Order | None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_full_entry(self, entry_id: int) -> Entry | None:
|
||||||
|
pass
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue