diff --git a/aurweb/db.py b/aurweb/db.py index 1f6f50d8..9837c746 100644 --- a/aurweb/db.py +++ b/aurweb/db.py @@ -1,7 +1,5 @@ import math -from sqlalchemy.orm import backref, relationship - import aurweb.config import aurweb.util @@ -53,12 +51,6 @@ def make_random_value(table: str, column: str): return string -def make_relationship(model, foreign_key: str, backref_: str, **kwargs): - return relationship(model, foreign_keys=[foreign_key], - backref=backref(backref_, lazy="dynamic"), - **kwargs) - - def query(model, *args, **kwargs): return session.query(model).filter(*args, **kwargs) @@ -77,6 +69,10 @@ def delete(model, *args, **kwargs): session.commit() +def rollback(): + session.rollback() + + def get_sqlalchemy_url(): """ Build an SQLAlchemy for use with create_engine based on the aurweb configuration. @@ -137,7 +133,6 @@ def get_engine(echo: bool = False): # check_same_thread is for a SQLite technicality # https://fastapi.tiangolo.com/tutorial/sql-databases/#note connect_args["check_same_thread"] = False - engine = create_engine(get_sqlalchemy_url(), connect_args=connect_args, echo=echo) diff --git a/aurweb/models/__init__.py b/aurweb/models/__init__.py index e69de29b..ed0532c6 100644 --- a/aurweb/models/__init__.py +++ b/aurweb/models/__init__.py @@ -0,0 +1 @@ +# aurweb SQLAlchemy ORM model collection. diff --git a/aurweb/models/accepted_term.py b/aurweb/models/accepted_term.py index 483109f1..b46d086b 100644 --- a/aurweb/models/accepted_term.py +++ b/aurweb/models/accepted_term.py @@ -1,15 +1,33 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.term import Term -from aurweb.models.user import User -from aurweb.schema import AcceptedTerms +import aurweb.models.term +import aurweb.models.user + +from aurweb.models.declarative import Base -class AcceptedTerm: +class AcceptedTerm(Base): + __tablename__ = "AcceptedTerms" + + UsersID = Column(Integer, ForeignKey("Users.ID", ondelete="CASCADE"), + nullable=False) + User = relationship( + "User", backref=backref("accepted_terms", lazy="dynamic"), + foreign_keys=[UsersID]) + + TermsID = Column(Integer, ForeignKey("Terms.ID", ondelete="CASCADE"), + nullable=False) + Term = relationship( + "Term", backref=backref("accepted_terms", lazy="dynamic"), + foreign_keys=[TermsID]) + + __mapper_args__ = {"primary_key": [TermsID]} + def __init__(self, - User: User = None, Term: Term = None, + User: aurweb.models.user.User = None, + Term: aurweb.models.term.Term = None, Revision: int = None): self.User = User if not self.User: @@ -26,12 +44,3 @@ class AcceptedTerm: params=("NULL")) self.Revision = Revision - - -properties = { - "User": make_relationship(User, AcceptedTerms.c.UsersID, "accepted_terms"), - "Term": make_relationship(Term, AcceptedTerms.c.TermsID, "accepted") -} - -mapper(AcceptedTerm, AcceptedTerms, properties=properties, - primary_key=[AcceptedTerms.c.UsersID, AcceptedTerms.c.TermsID]) diff --git a/aurweb/models/account_type.py b/aurweb/models/account_type.py index 44225e35..502a86b1 100644 --- a/aurweb/models/account_type.py +++ b/aurweb/models/account_type.py @@ -1,10 +1,15 @@ -from sqlalchemy.orm import mapper +from sqlalchemy import Column, Integer -from aurweb.schema import AccountTypes +from aurweb.models.declarative import Base -class AccountType: +class AccountType(Base): """ An ORM model of a single AccountTypes record. """ + __tablename__ = "AccountTypes" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} def __init__(self, **kwargs): self.AccountType = kwargs.pop("AccountType") @@ -15,6 +20,3 @@ class AccountType: def __repr__(self): return "" % ( self.ID, str(self)) - - -mapper(AccountType, AccountTypes, confirm_deleted_rows=False) diff --git a/aurweb/models/api_rate_limit.py b/aurweb/models/api_rate_limit.py index 8b945b6a..f4590553 100644 --- a/aurweb/models/api_rate_limit.py +++ b/aurweb/models/api_rate_limit.py @@ -1,11 +1,18 @@ +from sqlalchemy import Column, String from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper -from aurweb.schema import ApiRateLimit as _ApiRateLimit +from aurweb.models.declarative import Base -class ApiRateLimit: - def __init__(self, IP: str = None, +class ApiRateLimit(Base): + __tablename__ = "ApiRateLimit" + + IP = Column(String(45), primary_key=True, unique=True, default=str()) + + __mapper_args__ = {"primary_key": [IP]} + + def __init__(self, + IP: str = None, Requests: int = None, WindowStart: int = None): self.IP = IP @@ -23,6 +30,3 @@ class ApiRateLimit: statement="Column WindowStart cannot be null.", orig="ApiRateLimit.WindowStart", params=("NULL")) - - -mapper(ApiRateLimit, _ApiRateLimit, primary_key=[_ApiRateLimit.c.IP]) diff --git a/aurweb/models/ban.py b/aurweb/models/ban.py index be030380..e10087b0 100644 --- a/aurweb/models/ban.py +++ b/aurweb/models/ban.py @@ -1,10 +1,16 @@ from fastapi import Request -from sqlalchemy.orm import mapper +from sqlalchemy import Column, String -from aurweb.schema import Bans +from aurweb.models.declarative import Base -class Ban: +class Ban(Base): + __tablename__ = "Bans" + + IPAddress = Column(String(45), primary_key=True) + + __mapper_args__ = {"primary_key": [IPAddress]} + def __init__(self, **kwargs): self.IPAddress = kwargs.get("IPAddress") self.BanTS = kwargs.get("BanTS") @@ -14,6 +20,3 @@ def is_banned(request: Request): from aurweb.db import session ip = request.client.host return session.query(Ban).filter(Ban.IPAddress == ip).first() is not None - - -mapper(Ban, Bans) diff --git a/aurweb/models/declarative.py b/aurweb/models/declarative.py new file mode 100644 index 00000000..45a629ce --- /dev/null +++ b/aurweb/models/declarative.py @@ -0,0 +1,10 @@ +from sqlalchemy.ext.declarative import declarative_base + +import aurweb.db + +Base = declarative_base() +Base.__table_args__ = { + "autoload": True, + "autoload_with": aurweb.db.get_engine(), + "extend_existing": True +} diff --git a/aurweb/models/dependency_type.py b/aurweb/models/dependency_type.py index 87b38069..71acf368 100644 --- a/aurweb/models/dependency_type.py +++ b/aurweb/models/dependency_type.py @@ -1,11 +1,14 @@ -from sqlalchemy.orm import mapper +from sqlalchemy import Column, Integer -from aurweb.schema import DependencyTypes +from aurweb.models.declarative import Base -class DependencyType: +class DependencyType(Base): + __tablename__ = "DependencyTypes" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, Name: str = None): self.Name = Name - - -mapper(DependencyType, DependencyTypes) diff --git a/aurweb/models/group.py b/aurweb/models/group.py index c5583eb4..1bd3a402 100644 --- a/aurweb/models/group.py +++ b/aurweb/models/group.py @@ -1,10 +1,16 @@ +from sqlalchemy import Column, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper -from aurweb.schema import Groups +from aurweb.models.declarative import Base -class Group: +class Group(Base): + __tablename__ = "Groups" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, Name: str = None): self.Name = Name if not self.Name: @@ -12,6 +18,3 @@ class Group: statement="Column Name cannot be null.", orig="Groups.Name", params=("NULL")) - - -mapper(Group, Groups) diff --git a/aurweb/models/license.py b/aurweb/models/license.py index bcc02713..aef6a619 100644 --- a/aurweb/models/license.py +++ b/aurweb/models/license.py @@ -1,10 +1,16 @@ +from sqlalchemy import Column, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper -from aurweb.schema import Licenses +from aurweb.models.declarative import Base -class License: +class License(Base): + __tablename__ = "Licenses" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, Name: str = None): self.Name = Name if not self.Name: @@ -12,6 +18,3 @@ class License: statement="Column Name cannot be null.", orig="Licenses.Name", params=("NULL")) - - -mapper(License, Licenses) diff --git a/aurweb/models/official_provider.py b/aurweb/models/official_provider.py index 073eb435..756be843 100644 --- a/aurweb/models/official_provider.py +++ b/aurweb/models/official_provider.py @@ -1,10 +1,16 @@ +from sqlalchemy import Column, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper -from aurweb.schema import OfficialProviders +from aurweb.models.declarative import Base -class OfficialProvider: +class OfficialProvider(Base): + __tablename__ = "OfficialProviders" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, Name: str = None, Repo: str = None, @@ -29,6 +35,3 @@ class OfficialProvider: statement="Column Provides cannot be null.", orig="OfficialProviders.Provides", params=("NULL")) - - -mapper(OfficialProvider, OfficialProviders) diff --git a/aurweb/models/package.py b/aurweb/models/package.py index 28a13791..ff518f20 100644 --- a/aurweb/models/package.py +++ b/aurweb/models/package.py @@ -1,20 +1,37 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.package_base import PackageBase -from aurweb.schema import Packages +import aurweb.db +import aurweb.models.package_base + +from aurweb.models.declarative import Base -class Package: +class Package(Base): + __tablename__ = "Packages" + + ID = Column(Integer, primary_key=True) + + PackageBaseID = Column( + Integer, ForeignKey("PackageBases.ID", ondelete="CASCADE"), + nullable=False) + PackageBase = relationship( + "PackageBase", backref=backref("package", uselist=False), + foreign_keys=[PackageBaseID]) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, - PackageBase: PackageBase = None, - Name: str = None, Version: str = None, - Description: str = None, URL: str = None): + PackageBase: aurweb.models.package_base.PackageBase = None, + Name: str = None, + Version: str = None, + Description: str = None, + URL: str = None): self.PackageBase = PackageBase if not self.PackageBase: raise IntegrityError( - statement="Foreign key UserID cannot be null.", + statement="Foreign key PackageBaseID cannot be null.", orig="Packages.PackageBaseID", params=("NULL")) @@ -28,10 +45,3 @@ class Package: self.Version = Version self.Description = Description self.URL = URL - - -mapper(Package, Packages, properties={ - "PackageBase": make_relationship(PackageBase, - Packages.c.PackageBaseID, - "package", uselist=False) -}) diff --git a/aurweb/models/package_base.py b/aurweb/models/package_base.py index 699559d5..261c30f3 100644 --- a/aurweb/models/package_base.py +++ b/aurweb/models/package_base.py @@ -1,17 +1,47 @@ from datetime import datetime +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.user import User -from aurweb.schema import PackageBases +import aurweb.models.user + +from aurweb.models.declarative import Base -class PackageBase: - def __init__(self, Name: str = None, Flagger: User = None, - Maintainer: User = None, Submitter: User = None, - Packager: User = None, **kwargs): +class PackageBase(Base): + __tablename__ = "PackageBases" + + FlaggerUID = Column(Integer, + ForeignKey("Users.ID", ondelete="SET NULL")) + Flagger = relationship( + "User", backref=backref("flagged_bases", lazy="dynamic"), + foreign_keys=[FlaggerUID]) + + SubmitterUID = Column(Integer, + ForeignKey("Users.ID", ondelete="SET NULL")) + Submitter = relationship( + "User", backref=backref("submitted_bases", lazy="dynamic"), + foreign_keys=[SubmitterUID]) + + MaintainerUID = Column(Integer, + ForeignKey("Users.ID", ondelete="SET NULL")) + Maintainer = relationship( + "User", backref=backref("maintained_bases", lazy="dynamic"), + foreign_keys=[MaintainerUID]) + + PackagerUID = Column(Integer, ForeignKey("Users.ID", ondelete="SET NULL")) + Packager = relationship( + "User", backref=backref("package_bases", lazy="dynamic"), + foreign_keys=[PackagerUID]) + + def __init__(self, Name: str = None, + Flagger: aurweb.models.user.User = None, + Maintainer: aurweb.models.user.User = None, + Submitter: aurweb.models.user.User = None, + Packager: aurweb.models.user.User = None, + **kwargs): + super().__init__(**kwargs) self.Name = Name if not self.Name: raise IntegrityError( @@ -32,15 +62,3 @@ class PackageBase: datetime.utcnow().timestamp()) self.ModifiedTS = kwargs.get("ModifiedTS", datetime.utcnow().timestamp()) - - -mapper(PackageBase, PackageBases, properties={ - "Flagger": make_relationship(User, PackageBases.c.FlaggerUID, - "flagged_bases"), - "Submitter": make_relationship(User, PackageBases.c.SubmitterUID, - "submitted_bases"), - "Maintainer": make_relationship(User, PackageBases.c.MaintainerUID, - "maintained_bases"), - "Packager": make_relationship(User, PackageBases.c.PackagerUID, - "package_bases") -}) diff --git a/aurweb/models/package_dependency.py b/aurweb/models/package_dependency.py index 21801802..0bd84073 100644 --- a/aurweb/models/package_dependency.py +++ b/aurweb/models/package_dependency.py @@ -1,17 +1,40 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.dependency_type import DependencyType -from aurweb.models.package import Package -from aurweb.schema import PackageDepends +import aurweb.models.package + +from aurweb.models import dependency_type +from aurweb.models.declarative import Base -class PackageDependency: - def __init__(self, Package: Package = None, - DependencyType: DependencyType = None, - DepName: str = None, DepDesc: str = None, - DepCondition: str = None, DepArch: str = None): +class PackageDependency(Base): + __tablename__ = "PackageDepends" + + PackageID = Column( + Integer, ForeignKey("Packages.ID", ondelete="CASCADE"), + nullable=False) + Package = relationship( + "Package", backref=backref("package_dependencies", lazy="dynamic"), + foreign_keys=[PackageID], lazy="select") + + DepTypeID = Column( + Integer, ForeignKey("DependencyTypes.ID", ondelete="NO ACTION"), + nullable=False) + DependencyType = relationship( + "DependencyType", + backref=backref("package_dependencies", lazy="dynamic"), + foreign_keys=[DepTypeID], lazy="select") + + __mapper_args__ = {"primary_key": [PackageID, DepTypeID]} + + def __init__(self, + Package: aurweb.models.package.Package = None, + DependencyType: dependency_type.DependencyType = None, + DepName: str = None, + DepDesc: str = None, + DepCondition: str = None, + DepArch: str = None): self.Package = Package if not self.Package: raise IntegrityError( @@ -36,15 +59,3 @@ class PackageDependency: self.DepDesc = DepDesc self.DepCondition = DepCondition self.DepArch = DepArch - - -properties = { - "Package": make_relationship(Package, PackageDepends.c.PackageID, - "package_dependencies"), - "DependencyType": make_relationship(DependencyType, - PackageDepends.c.DepTypeID, - "package_dependencies") -} - -mapper(PackageDependency, PackageDepends, properties=properties, - primary_key=[PackageDepends.c.PackageID, PackageDepends.c.DepTypeID]) diff --git a/aurweb/models/package_group.py b/aurweb/models/package_group.py index 19a11c80..a8031e0d 100644 --- a/aurweb/models/package_group.py +++ b/aurweb/models/package_group.py @@ -1,14 +1,33 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.group import Group -from aurweb.models.package import Package -from aurweb.schema import PackageGroups +import aurweb.models.group +import aurweb.models.package + +from aurweb.models.declarative import Base -class PackageGroup: - def __init__(self, Package: Package = None, Group: Group = None): +class PackageGroup(Base): + __tablename__ = "PackageGroups" + + PackageID = Column(Integer, ForeignKey("Packages.ID", ondelete="CASCADE"), + primary_key=True, nullable=True) + Package = relationship( + "Package", backref=backref("package_groups", lazy="dynamic"), + foreign_keys=[PackageID]) + + GroupID = Column(Integer, ForeignKey("Groups.ID", ondelete="CASCADE"), + primary_key=True, nullable=True) + Group = relationship( + "Group", backref=backref("package_groups", lazy="dynamic"), + foreign_keys=[GroupID]) + + __mapper_args__ = {"primary_key": [PackageID, GroupID]} + + def __init__(self, + Package: aurweb.models.package.Package = None, + Group: aurweb.models.group.Group = None): self.Package = Package if not self.Package: raise IntegrityError( @@ -22,18 +41,3 @@ class PackageGroup: statement="Primary key GroupID cannot be null.", orig="PackageGroups.GroupID", params=("NULL")) - - -properties = { - "Package": make_relationship(Package, - PackageGroups.c.PackageID, - "package_group", - uselist=False), - "Group": make_relationship(Group, - PackageGroups.c.GroupID, - "package_group", - uselist=False) -} - -mapper(PackageGroup, PackageGroups, properties=properties, - primary_key=[PackageGroups.c.PackageID, PackageGroups.c.GroupID]) diff --git a/aurweb/models/package_keyword.py b/aurweb/models/package_keyword.py index 2bae223c..2926740d 100644 --- a/aurweb/models/package_keyword.py +++ b/aurweb/models/package_keyword.py @@ -1,14 +1,27 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.package_base import PackageBase -from aurweb.schema import PackageKeywords +import aurweb.db +import aurweb.models.package_base + +from aurweb.models.declarative import Base -class PackageKeyword: +class PackageKeyword(Base): + __tablename__ = "PackageKeywords" + + PackageBaseID = Column( + Integer, ForeignKey("PackageBases.ID", ondelete="CASCADE"), + primary_key=True, nullable=True) + PackageBase = relationship( + "PackageBase", backref=backref("keywords", lazy="dynamic"), + foreign_keys=[PackageBaseID]) + + __mapper_args__ = {"primary_key": [PackageBaseID]} + def __init__(self, - PackageBase: PackageBase = None, + PackageBase: aurweb.models.package_base.PackageBase = None, Keyword: str = None): self.PackageBase = PackageBase if not self.PackageBase: @@ -18,10 +31,3 @@ class PackageKeyword: params=("NULL")) self.Keyword = Keyword - - -mapper(PackageKeyword, PackageKeywords, properties={ - "PackageBase": make_relationship(PackageBase, - PackageKeywords.c.PackageBaseID, - "keywords") -}) diff --git a/aurweb/models/package_license.py b/aurweb/models/package_license.py index 491874a4..0689562f 100644 --- a/aurweb/models/package_license.py +++ b/aurweb/models/package_license.py @@ -1,14 +1,35 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.license import License -from aurweb.models.package import Package -from aurweb.schema import PackageLicenses +import aurweb.models.license +import aurweb.models.package + +from aurweb.models.declarative import Base -class PackageLicense: - def __init__(self, Package: Package = None, License: License = None): +class PackageLicense(Base): + __tablename__ = "PackageLicenses" + + PackageID = Column( + Integer, ForeignKey("Packages.ID", ondelete="CASCADE"), + primary_key=True, nullable=True) + Package = relationship( + "Package", backref=backref("package_license", uselist=False), + foreign_keys=[PackageID]) + + LicenseID = Column( + Integer, ForeignKey("Licenses.ID", ondelete="CASCADE"), + primary_key=True, nullable=True) + License = relationship( + "License", backref=backref("package_license", uselist=False), + foreign_keys=[LicenseID]) + + __mapper_args__ = {"primary_key": [PackageID, LicenseID]} + + def __init__(self, + Package: aurweb.models.package.Package = None, + License: aurweb.models.license.License = None): self.Package = Package if not self.Package: raise IntegrityError( @@ -22,20 +43,3 @@ class PackageLicense: statement="Primary key LicenseID cannot be null.", orig="PackageLicenses.LicenseID", params=("NULL")) - - -properties = { - "Package": make_relationship(Package, - PackageLicenses.c.PackageID, - "package_license", - uselist=False), - "License": make_relationship(License, - PackageLicenses.c.LicenseID, - "package_license", - uselist=False) - - -} - -mapper(PackageLicense, PackageLicenses, properties=properties, - primary_key=[PackageLicenses.c.PackageID, PackageLicenses.c.LicenseID]) diff --git a/aurweb/models/package_relation.py b/aurweb/models/package_relation.py index d9ade727..9204af59 100644 --- a/aurweb/models/package_relation.py +++ b/aurweb/models/package_relation.py @@ -1,15 +1,36 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper +from sqlalchemy.orm import backref, relationship -from aurweb.db import make_relationship -from aurweb.models.package import Package -from aurweb.models.relation_type import RelationType -from aurweb.schema import PackageRelations +import aurweb.db +import aurweb.models.package +import aurweb.models.relation_type + +from aurweb.models.declarative import Base -class PackageRelation: - def __init__(self, Package: Package = None, - RelationType: RelationType = None, +class PackageRelation(Base): + __tablename__ = "PackageRelations" + + PackageID = Column( + Integer, ForeignKey("Packages.ID", ondelete="CASCADE"), + nullable=False) + Package = relationship( + "Package", backref=backref("package_relations", lazy="dynamic"), + foreign_keys=[PackageID]) + + RelTypeID = Column( + Integer, ForeignKey("RelationTypes.ID", ondelete="CASCADE"), + nullable=False) + RelationType = relationship( + "RelationType", backref=backref("package_relations", lazy="dynamic"), + foreign_keys=[RelTypeID]) + + __mapper_args__ = {"primary_key": [PackageID, RelTypeID]} + + def __init__(self, + Package: aurweb.models.package.Package = None, + RelationType: aurweb.models.relation_type.RelationType = None, RelName: str = None, RelCondition: str = None, RelArch: str = None): self.Package = Package @@ -35,18 +56,3 @@ class PackageRelation: self.RelCondition = RelCondition self.RelArch = RelArch - - -properties = { - "Package": make_relationship(Package, PackageRelations.c.PackageID, - "package_relations"), - "RelationType": make_relationship(RelationType, - PackageRelations.c.RelTypeID, - "package_relations") -} - -mapper(PackageRelation, PackageRelations, properties=properties, - primary_key=[ - PackageRelations.c.PackageID, - PackageRelations.c.RelTypeID - ]) diff --git a/aurweb/models/relation_type.py b/aurweb/models/relation_type.py index b4d1efbc..319fb7f4 100644 --- a/aurweb/models/relation_type.py +++ b/aurweb/models/relation_type.py @@ -1,11 +1,14 @@ -from sqlalchemy.orm import mapper +from sqlalchemy import Column, Integer -from aurweb.schema import RelationTypes +from aurweb.models.declarative import Base -class RelationType: +class RelationType(Base): + __tablename__ = "RelationTypes" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, Name: str = None): self.Name = Name - - -mapper(RelationType, RelationTypes) diff --git a/aurweb/models/session.py b/aurweb/models/session.py index f1e0fff5..9154178e 100644 --- a/aurweb/models/session.py +++ b/aurweb/models/session.py @@ -1,12 +1,24 @@ +from sqlalchemy import Column, ForeignKey, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import backref, mapper, relationship +from sqlalchemy.orm import backref, relationship from aurweb.db import make_random_value, query +from aurweb.models.declarative import Base from aurweb.models.user import User -from aurweb.schema import Sessions -class Session: +class Session(Base): + __tablename__ = "Sessions" + + UsersID = Column( + Integer, ForeignKey("Users.ID", ondelete="CASCADE"), + nullable=False) + User = relationship( + "User", backref=backref("session", uselist=False), + foreign_keys=[UsersID]) + + __mapper_args__ = {"primary_key": [UsersID]} + def __init__(self, **kwargs): self.UsersID = kwargs.get("UsersID") if not query(User, User.ID == self.UsersID).first(): @@ -19,11 +31,5 @@ class Session: self.LastUpdateTS = kwargs.get("LastUpdateTS") -mapper(Session, Sessions, primary_key=[Sessions.c.SessionID], properties={ - "User": relationship(User, backref=backref("session", - uselist=False)) -}) - - def generate_unique_sid(): return make_random_value(Session, Session.SessionID) diff --git a/aurweb/models/ssh_pub_key.py b/aurweb/models/ssh_pub_key.py index 01ff558e..268a585b 100644 --- a/aurweb/models/ssh_pub_key.py +++ b/aurweb/models/ssh_pub_key.py @@ -3,13 +3,26 @@ import tempfile from subprocess import PIPE, Popen -from sqlalchemy.orm import backref, mapper, relationship +from sqlalchemy import Column, ForeignKey, Integer, String +from sqlalchemy.orm import backref, relationship -from aurweb.models.user import User -from aurweb.schema import SSHPubKeys +from aurweb.models.declarative import Base -class SSHPubKey: +class SSHPubKey(Base): + __tablename__ = "SSHPubKeys" + + UserID = Column( + Integer, ForeignKey("Users.ID", ondelete="CASCADE"), + nullable=False) + User = relationship( + "User", backref=backref("ssh_pub_key", uselist=False), + foreign_keys=[UserID]) + + Fingerprint = Column(String(44), primary_key=True) + + __mapper_args__ = {"primary_key": Fingerprint} + def __init__(self, **kwargs): self.UserID = kwargs.get("UserID") self.Fingerprint = kwargs.get("Fingerprint") @@ -34,8 +47,3 @@ def get_fingerprint(pubkey): fp = parts[1].replace("SHA256:", "") return fp - - -mapper(SSHPubKey, SSHPubKeys, properties={ - "User": relationship(User, backref=backref("ssh_pub_key", uselist=False)) -}) diff --git a/aurweb/models/term.py b/aurweb/models/term.py index 1a0780df..b0da71f7 100644 --- a/aurweb/models/term.py +++ b/aurweb/models/term.py @@ -1,10 +1,16 @@ +from sqlalchemy import Column, Integer from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import mapper -from aurweb.schema import Terms +from aurweb.models.declarative import Base -class Term: +class Term(Base): + __tablename__ = "Terms" + + ID = Column(Integer, primary_key=True) + + __mapper_args__ = {"primary_key": [ID]} + def __init__(self, Description: str = None, URL: str = None, Revision: int = None): @@ -23,6 +29,3 @@ class Term: params=("NULL")) self.Revision = Revision - - -mapper(Term, Terms) diff --git a/aurweb/models/user.py b/aurweb/models/user.py index 1961228e..83cde5f1 100644 --- a/aurweb/models/user.py +++ b/aurweb/models/user.py @@ -5,50 +5,44 @@ from datetime import datetime import bcrypt from fastapi import Request -from sqlalchemy.orm import backref, mapper, relationship +from sqlalchemy import Column, ForeignKey, Integer, String, text +from sqlalchemy.orm import backref, relationship import aurweb.config +import aurweb.models.account_type +import aurweb.schema -from aurweb.models.account_type import AccountType from aurweb.models.ban import is_banned -from aurweb.schema import Users +from aurweb.models.declarative import Base -class User: +class User(Base): """ An ORM model of a single Users record. """ + __tablename__ = "Users" + + ID = Column(Integer, primary_key=True) + + AccountTypeID = Column( + Integer, ForeignKey("AccountTypes.ID", ondelete="NO ACTION"), + nullable=False, server_default=text("1")) + AccountType = relationship( + "AccountType", + backref=backref("users", lazy="dynamic"), + foreign_keys=[AccountTypeID], + uselist=False) + + Passwd = Column(String(255), default=str()) + + __mapper_args__ = {"primary_key": [ID]} + + # High-level variables used to track authentication (not in DB). authenticated = False - def __init__(self, **kwargs): - # Set AccountTypeID if it was passed. - self.AccountTypeID = kwargs.get("AccountTypeID") + def __init__(self, Passwd: str = str(), **kwargs): + super().__init__(**kwargs) - account_type = kwargs.get("AccountType") - if account_type: - self.AccountType = account_type - - self.Username = kwargs.get("Username") - - self.ResetKey = kwargs.get("ResetKey") - self.Email = kwargs.get("Email") - self.BackupEmail = kwargs.get("BackupEmail") - self.RealName = kwargs.get("RealName") - self.LangPreference = kwargs.get("LangPreference") - self.Timezone = kwargs.get("Timezone") - self.Homepage = kwargs.get("Homepage") - self.IRCNick = kwargs.get("IRCNick") - self.PGPKey = kwargs.get("PGPKey") - self.RegistrationTS = datetime.utcnow() - self.CommentNotify = kwargs.get("CommentNotify") - self.UpdateNotify = kwargs.get("UpdateNotify") - self.OwnershipNotify = kwargs.get("OwnershipNotify") - self.SSOAccountID = kwargs.get("SSOAccountID") - - self.Salt = None - self.Passwd = str() - - passwd = kwargs.get("Passwd") - if passwd: - self.update_password(passwd) + if Passwd: + self.update_password(Passwd) def update_password(self, password, salt_rounds=12): self.Passwd = bcrypt.hashpw( @@ -154,10 +148,3 @@ class User: def __repr__(self): return "" % ( self.ID, str(self.AccountType), self.Username) - - -# Map schema.Users to User and give it some relationships. -mapper(User, Users, properties={ - "AccountType": relationship(AccountType, - backref=backref("users", lazy="dynamic")) -}) diff --git a/test/test_accepted_term.py b/test/test_accepted_term.py index 4ddf1fc3..8569b021 100644 --- a/test/test_accepted_term.py +++ b/test/test_accepted_term.py @@ -22,7 +22,7 @@ def setup(): AccountType.AccountType == "User").first() user = create(User, Username="test", Email="test@example.org", RealName="Test User", Passwd="testPassword", - account_type=account_type) + AccountType=account_type) term = create(Term, Description="Test term", URL="https://test.term") @@ -33,7 +33,7 @@ def test_accepted_term(): # Make sure our AcceptedTerm relationships got initialized properly. assert accepted_term.User == user assert accepted_term in user.accepted_terms - assert accepted_term in term.accepted + assert accepted_term in term.accepted_terms def test_accepted_term_null_user_raises_exception(): diff --git a/test/test_account_type.py b/test/test_account_type.py index 3bd76d1e..fa4bc5ad 100644 --- a/test/test_account_type.py +++ b/test/test_account_type.py @@ -43,6 +43,7 @@ def test_user_account_type_relationship(): AccountType=account_type) assert user.AccountType == account_type - assert account_type.users.filter(User.ID == user.ID).first() + # This must be deleted here to avoid foreign key issues when + # deleting the temporary AccountType in the fixture. delete(User, User.ID == user.ID) diff --git a/test/test_package.py b/test/test_package.py index a994f096..9532823d 100644 --- a/test/test_package.py +++ b/test/test_package.py @@ -1,7 +1,7 @@ import pytest from sqlalchemy import and_ -from sqlalchemy.exc import IntegrityError +from sqlalchemy.exc import IntegrityError, OperationalError import aurweb.config @@ -19,7 +19,7 @@ user = pkgbase = package = None def setup(): global user, pkgbase, package - setup_test_db("Users", "PackageBases", "Packages") + setup_test_db("Packages", "PackageBases", "Users") account_type = query(AccountType, AccountType.AccountType == "User").first() @@ -57,17 +57,17 @@ def test_package(): assert record is not None -def test_package_ci(): +def test_package_package_base_cant_change(): """ Test case insensitivity of the database table. """ if aurweb.config.get("database", "backend") == "sqlite": return None # SQLite doesn't seem handle this. from aurweb.db import session - with pytest.raises(IntegrityError): + with pytest.raises(OperationalError): create(Package, PackageBase=pkgbase, - Name="Beautiful-Package") + Name="invalidates-old-package-packagebase-relationship") session.rollback() diff --git a/test/test_package_group.py b/test/test_package_group.py index 28047a7f..0e6e41e3 100644 --- a/test/test_package_group.py +++ b/test/test_package_group.py @@ -25,7 +25,7 @@ def setup(): AccountType.AccountType == "User").first() user = create(User, Username="test", Email="test@example.org", RealName="Test User", Passwd="testPassword", - account_type=account_type) + AccountType=account_type) group = create(Group, Name="Test Group") pkgbase = create(PackageBase, Name="test-package", Maintainer=user) diff --git a/test/test_package_license.py b/test/test_package_license.py index 72eb3681..f7654dee 100644 --- a/test/test_package_license.py +++ b/test/test_package_license.py @@ -25,7 +25,7 @@ def setup(): AccountType.AccountType == "User").first() user = create(User, Username="test", Email="test@example.org", RealName="Test User", Passwd="testPassword", - account_type=account_type) + AccountType=account_type) license = create(License, Name="Test License") pkgbase = create(PackageBase, Name="test-package", Maintainer=user) diff --git a/test/test_session.py b/test/test_session.py index 1dd82db1..1ba11556 100644 --- a/test/test_session.py +++ b/test/test_session.py @@ -10,12 +10,12 @@ from aurweb.models.session import Session, generate_unique_sid from aurweb.models.user import User from aurweb.testing import setup_test_db -user = session = None +account_type = user = session = None @pytest.fixture(autouse=True) def setup(): - global user, session + global account_type, user, session setup_test_db("Users", "Sessions") @@ -35,7 +35,10 @@ def test_session(): def test_session_cs(): """ Test case sensitivity of the database table. """ - session_cs = create(Session, UsersID=user.ID, + user2 = create(User, Username="test2", Email="test2@example.org", + ResetKey="testReset2", Passwd="testPassword", + AccountType=account_type) + session_cs = create(Session, UsersID=user2.ID, SessionID="TESTSESSION", LastUpdateTS=datetime.utcnow().timestamp()) assert session_cs.SessionID == "TESTSESSION"