use declarative_base for all ORM models

This rewrites the entire model base as declarative models.
This allows us to more easily customize overlay fields
in tables and is more common.

This effort also brought some DB violations to light which
this commit addresses.

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-06-06 22:45:40 -07:00
parent 7f7a975614
commit 888cf5118a
29 changed files with 398 additions and 292 deletions

View file

@ -1,7 +1,5 @@
import math import math
from sqlalchemy.orm import backref, relationship
import aurweb.config import aurweb.config
import aurweb.util import aurweb.util
@ -53,12 +51,6 @@ def make_random_value(table: str, column: str):
return string 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): def query(model, *args, **kwargs):
return session.query(model).filter(*args, **kwargs) return session.query(model).filter(*args, **kwargs)
@ -77,6 +69,10 @@ def delete(model, *args, **kwargs):
session.commit() session.commit()
def rollback():
session.rollback()
def get_sqlalchemy_url(): def get_sqlalchemy_url():
""" """
Build an SQLAlchemy for use with create_engine based on the aurweb configuration. 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 # check_same_thread is for a SQLite technicality
# https://fastapi.tiangolo.com/tutorial/sql-databases/#note # https://fastapi.tiangolo.com/tutorial/sql-databases/#note
connect_args["check_same_thread"] = False connect_args["check_same_thread"] = False
engine = create_engine(get_sqlalchemy_url(), engine = create_engine(get_sqlalchemy_url(),
connect_args=connect_args, connect_args=connect_args,
echo=echo) echo=echo)

View file

@ -0,0 +1 @@
# aurweb SQLAlchemy ORM model collection.

View file

@ -1,15 +1,33 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.models.term
from aurweb.models.term import Term import aurweb.models.user
from aurweb.models.user import User
from aurweb.schema import AcceptedTerms 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, def __init__(self,
User: User = None, Term: Term = None, User: aurweb.models.user.User = None,
Term: aurweb.models.term.Term = None,
Revision: int = None): Revision: int = None):
self.User = User self.User = User
if not self.User: if not self.User:
@ -26,12 +44,3 @@ class AcceptedTerm:
params=("NULL")) params=("NULL"))
self.Revision = Revision 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])

View file

@ -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. """ """ 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): def __init__(self, **kwargs):
self.AccountType = kwargs.pop("AccountType") self.AccountType = kwargs.pop("AccountType")
@ -15,6 +20,3 @@ class AccountType:
def __repr__(self): def __repr__(self):
return "<AccountType(ID='%s', AccountType='%s')>" % ( return "<AccountType(ID='%s', AccountType='%s')>" % (
self.ID, str(self)) self.ID, str(self))
mapper(AccountType, AccountTypes, confirm_deleted_rows=False)

View file

@ -1,11 +1,18 @@
from sqlalchemy import Column, String
from sqlalchemy.exc import IntegrityError 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: class ApiRateLimit(Base):
def __init__(self, IP: str = None, __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, Requests: int = None,
WindowStart: int = None): WindowStart: int = None):
self.IP = IP self.IP = IP
@ -23,6 +30,3 @@ class ApiRateLimit:
statement="Column WindowStart cannot be null.", statement="Column WindowStart cannot be null.",
orig="ApiRateLimit.WindowStart", orig="ApiRateLimit.WindowStart",
params=("NULL")) params=("NULL"))
mapper(ApiRateLimit, _ApiRateLimit, primary_key=[_ApiRateLimit.c.IP])

View file

@ -1,10 +1,16 @@
from fastapi import Request 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): def __init__(self, **kwargs):
self.IPAddress = kwargs.get("IPAddress") self.IPAddress = kwargs.get("IPAddress")
self.BanTS = kwargs.get("BanTS") self.BanTS = kwargs.get("BanTS")
@ -14,6 +20,3 @@ def is_banned(request: Request):
from aurweb.db import session from aurweb.db import session
ip = request.client.host ip = request.client.host
return session.query(Ban).filter(Ban.IPAddress == ip).first() is not None return session.query(Ban).filter(Ban.IPAddress == ip).first() is not None
mapper(Ban, Bans)

View file

@ -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
}

View file

@ -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): def __init__(self, Name: str = None):
self.Name = Name self.Name = Name
mapper(DependencyType, DependencyTypes)

View file

@ -1,10 +1,16 @@
from sqlalchemy import Column, Integer
from sqlalchemy.exc import IntegrityError 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): def __init__(self, Name: str = None):
self.Name = Name self.Name = Name
if not self.Name: if not self.Name:
@ -12,6 +18,3 @@ class Group:
statement="Column Name cannot be null.", statement="Column Name cannot be null.",
orig="Groups.Name", orig="Groups.Name",
params=("NULL")) params=("NULL"))
mapper(Group, Groups)

View file

@ -1,10 +1,16 @@
from sqlalchemy import Column, Integer
from sqlalchemy.exc import IntegrityError 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): def __init__(self, Name: str = None):
self.Name = Name self.Name = Name
if not self.Name: if not self.Name:
@ -12,6 +18,3 @@ class License:
statement="Column Name cannot be null.", statement="Column Name cannot be null.",
orig="Licenses.Name", orig="Licenses.Name",
params=("NULL")) params=("NULL"))
mapper(License, Licenses)

View file

@ -1,10 +1,16 @@
from sqlalchemy import Column, Integer
from sqlalchemy.exc import IntegrityError 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, def __init__(self,
Name: str = None, Name: str = None,
Repo: str = None, Repo: str = None,
@ -29,6 +35,3 @@ class OfficialProvider:
statement="Column Provides cannot be null.", statement="Column Provides cannot be null.",
orig="OfficialProviders.Provides", orig="OfficialProviders.Provides",
params=("NULL")) params=("NULL"))
mapper(OfficialProvider, OfficialProviders)

View file

@ -1,20 +1,37 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.db
from aurweb.models.package_base import PackageBase import aurweb.models.package_base
from aurweb.schema import Packages
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, def __init__(self,
PackageBase: PackageBase = None, PackageBase: aurweb.models.package_base.PackageBase = None,
Name: str = None, Version: str = None, Name: str = None,
Description: str = None, URL: str = None): Version: str = None,
Description: str = None,
URL: str = None):
self.PackageBase = PackageBase self.PackageBase = PackageBase
if not self.PackageBase: if not self.PackageBase:
raise IntegrityError( raise IntegrityError(
statement="Foreign key UserID cannot be null.", statement="Foreign key PackageBaseID cannot be null.",
orig="Packages.PackageBaseID", orig="Packages.PackageBaseID",
params=("NULL")) params=("NULL"))
@ -28,10 +45,3 @@ class Package:
self.Version = Version self.Version = Version
self.Description = Description self.Description = Description
self.URL = URL self.URL = URL
mapper(Package, Packages, properties={
"PackageBase": make_relationship(PackageBase,
Packages.c.PackageBaseID,
"package", uselist=False)
})

View file

@ -1,17 +1,47 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.models.user
from aurweb.models.user import User
from aurweb.schema import PackageBases from aurweb.models.declarative import Base
class PackageBase: class PackageBase(Base):
def __init__(self, Name: str = None, Flagger: User = None, __tablename__ = "PackageBases"
Maintainer: User = None, Submitter: User = None,
Packager: User = None, **kwargs): 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 self.Name = Name
if not self.Name: if not self.Name:
raise IntegrityError( raise IntegrityError(
@ -32,15 +62,3 @@ class PackageBase:
datetime.utcnow().timestamp()) datetime.utcnow().timestamp())
self.ModifiedTS = kwargs.get("ModifiedTS", self.ModifiedTS = kwargs.get("ModifiedTS",
datetime.utcnow().timestamp()) 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")
})

View file

@ -1,17 +1,40 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.models.package
from aurweb.models.dependency_type import DependencyType
from aurweb.models.package import Package from aurweb.models import dependency_type
from aurweb.schema import PackageDepends from aurweb.models.declarative import Base
class PackageDependency: class PackageDependency(Base):
def __init__(self, Package: Package = None, __tablename__ = "PackageDepends"
DependencyType: DependencyType = None,
DepName: str = None, DepDesc: str = None, PackageID = Column(
DepCondition: str = None, DepArch: str = None): 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 self.Package = Package
if not self.Package: if not self.Package:
raise IntegrityError( raise IntegrityError(
@ -36,15 +59,3 @@ class PackageDependency:
self.DepDesc = DepDesc self.DepDesc = DepDesc
self.DepCondition = DepCondition self.DepCondition = DepCondition
self.DepArch = DepArch 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])

View file

@ -1,14 +1,33 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.models.group
from aurweb.models.group import Group import aurweb.models.package
from aurweb.models.package import Package
from aurweb.schema import PackageGroups from aurweb.models.declarative import Base
class PackageGroup: class PackageGroup(Base):
def __init__(self, Package: Package = None, Group: Group = None): __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 self.Package = Package
if not self.Package: if not self.Package:
raise IntegrityError( raise IntegrityError(
@ -22,18 +41,3 @@ class PackageGroup:
statement="Primary key GroupID cannot be null.", statement="Primary key GroupID cannot be null.",
orig="PackageGroups.GroupID", orig="PackageGroups.GroupID",
params=("NULL")) 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])

View file

@ -1,14 +1,27 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.db
from aurweb.models.package_base import PackageBase import aurweb.models.package_base
from aurweb.schema import PackageKeywords
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, def __init__(self,
PackageBase: PackageBase = None, PackageBase: aurweb.models.package_base.PackageBase = None,
Keyword: str = None): Keyword: str = None):
self.PackageBase = PackageBase self.PackageBase = PackageBase
if not self.PackageBase: if not self.PackageBase:
@ -18,10 +31,3 @@ class PackageKeyword:
params=("NULL")) params=("NULL"))
self.Keyword = Keyword self.Keyword = Keyword
mapper(PackageKeyword, PackageKeywords, properties={
"PackageBase": make_relationship(PackageBase,
PackageKeywords.c.PackageBaseID,
"keywords")
})

View file

@ -1,14 +1,35 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.models.license
from aurweb.models.license import License import aurweb.models.package
from aurweb.models.package import Package
from aurweb.schema import PackageLicenses from aurweb.models.declarative import Base
class PackageLicense: class PackageLicense(Base):
def __init__(self, Package: Package = None, License: License = None): __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 self.Package = Package
if not self.Package: if not self.Package:
raise IntegrityError( raise IntegrityError(
@ -22,20 +43,3 @@ class PackageLicense:
statement="Primary key LicenseID cannot be null.", statement="Primary key LicenseID cannot be null.",
orig="PackageLicenses.LicenseID", orig="PackageLicenses.LicenseID",
params=("NULL")) 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])

View file

@ -1,15 +1,36 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import mapper from sqlalchemy.orm import backref, relationship
from aurweb.db import make_relationship import aurweb.db
from aurweb.models.package import Package import aurweb.models.package
from aurweb.models.relation_type import RelationType import aurweb.models.relation_type
from aurweb.schema import PackageRelations
from aurweb.models.declarative import Base
class PackageRelation: class PackageRelation(Base):
def __init__(self, Package: Package = None, __tablename__ = "PackageRelations"
RelationType: RelationType = None,
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, RelName: str = None, RelCondition: str = None,
RelArch: str = None): RelArch: str = None):
self.Package = Package self.Package = Package
@ -35,18 +56,3 @@ class PackageRelation:
self.RelCondition = RelCondition self.RelCondition = RelCondition
self.RelArch = RelArch 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
])

View file

@ -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): def __init__(self, Name: str = None):
self.Name = Name self.Name = Name
mapper(RelationType, RelationTypes)

View file

@ -1,12 +1,24 @@
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.exc import IntegrityError 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.db import make_random_value, query
from aurweb.models.declarative import Base
from aurweb.models.user import User 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): def __init__(self, **kwargs):
self.UsersID = kwargs.get("UsersID") self.UsersID = kwargs.get("UsersID")
if not query(User, User.ID == self.UsersID).first(): if not query(User, User.ID == self.UsersID).first():
@ -19,11 +31,5 @@ class Session:
self.LastUpdateTS = kwargs.get("LastUpdateTS") 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(): def generate_unique_sid():
return make_random_value(Session, Session.SessionID) return make_random_value(Session, Session.SessionID)

View file

@ -3,13 +3,26 @@ import tempfile
from subprocess import PIPE, Popen 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.models.declarative import Base
from aurweb.schema import SSHPubKeys
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): def __init__(self, **kwargs):
self.UserID = kwargs.get("UserID") self.UserID = kwargs.get("UserID")
self.Fingerprint = kwargs.get("Fingerprint") self.Fingerprint = kwargs.get("Fingerprint")
@ -34,8 +47,3 @@ def get_fingerprint(pubkey):
fp = parts[1].replace("SHA256:", "") fp = parts[1].replace("SHA256:", "")
return fp return fp
mapper(SSHPubKey, SSHPubKeys, properties={
"User": relationship(User, backref=backref("ssh_pub_key", uselist=False))
})

View file

@ -1,10 +1,16 @@
from sqlalchemy import Column, Integer
from sqlalchemy.exc import IntegrityError 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, def __init__(self,
Description: str = None, URL: str = None, Description: str = None, URL: str = None,
Revision: int = None): Revision: int = None):
@ -23,6 +29,3 @@ class Term:
params=("NULL")) params=("NULL"))
self.Revision = Revision self.Revision = Revision
mapper(Term, Terms)

View file

@ -5,50 +5,44 @@ from datetime import datetime
import bcrypt import bcrypt
from fastapi import Request 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.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.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. """ """ 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 authenticated = False
def __init__(self, **kwargs): def __init__(self, Passwd: str = str(), **kwargs):
# Set AccountTypeID if it was passed. super().__init__(**kwargs)
self.AccountTypeID = kwargs.get("AccountTypeID")
account_type = kwargs.get("AccountType") if Passwd:
if account_type: self.update_password(Passwd)
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)
def update_password(self, password, salt_rounds=12): def update_password(self, password, salt_rounds=12):
self.Passwd = bcrypt.hashpw( self.Passwd = bcrypt.hashpw(
@ -154,10 +148,3 @@ class User:
def __repr__(self): def __repr__(self):
return "<User(ID='%s', AccountType='%s', Username='%s')>" % ( return "<User(ID='%s', AccountType='%s', Username='%s')>" % (
self.ID, str(self.AccountType), self.Username) 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"))
})

View file

@ -22,7 +22,7 @@ def setup():
AccountType.AccountType == "User").first() AccountType.AccountType == "User").first()
user = create(User, Username="test", Email="test@example.org", user = create(User, Username="test", Email="test@example.org",
RealName="Test User", Passwd="testPassword", RealName="Test User", Passwd="testPassword",
account_type=account_type) AccountType=account_type)
term = create(Term, Description="Test term", URL="https://test.term") 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. # Make sure our AcceptedTerm relationships got initialized properly.
assert accepted_term.User == user assert accepted_term.User == user
assert accepted_term in user.accepted_terms 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(): def test_accepted_term_null_user_raises_exception():

View file

@ -43,6 +43,7 @@ def test_user_account_type_relationship():
AccountType=account_type) AccountType=account_type)
assert user.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) delete(User, User.ID == user.ID)

View file

@ -1,7 +1,7 @@
import pytest import pytest
from sqlalchemy import and_ from sqlalchemy import and_
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError, OperationalError
import aurweb.config import aurweb.config
@ -19,7 +19,7 @@ user = pkgbase = package = None
def setup(): def setup():
global user, pkgbase, package global user, pkgbase, package
setup_test_db("Users", "PackageBases", "Packages") setup_test_db("Packages", "PackageBases", "Users")
account_type = query(AccountType, account_type = query(AccountType,
AccountType.AccountType == "User").first() AccountType.AccountType == "User").first()
@ -57,17 +57,17 @@ def test_package():
assert record is not None assert record is not None
def test_package_ci(): def test_package_package_base_cant_change():
""" Test case insensitivity of the database table. """ """ Test case insensitivity of the database table. """
if aurweb.config.get("database", "backend") == "sqlite": if aurweb.config.get("database", "backend") == "sqlite":
return None # SQLite doesn't seem handle this. return None # SQLite doesn't seem handle this.
from aurweb.db import session from aurweb.db import session
with pytest.raises(IntegrityError): with pytest.raises(OperationalError):
create(Package, create(Package,
PackageBase=pkgbase, PackageBase=pkgbase,
Name="Beautiful-Package") Name="invalidates-old-package-packagebase-relationship")
session.rollback() session.rollback()

View file

@ -25,7 +25,7 @@ def setup():
AccountType.AccountType == "User").first() AccountType.AccountType == "User").first()
user = create(User, Username="test", Email="test@example.org", user = create(User, Username="test", Email="test@example.org",
RealName="Test User", Passwd="testPassword", RealName="Test User", Passwd="testPassword",
account_type=account_type) AccountType=account_type)
group = create(Group, Name="Test Group") group = create(Group, Name="Test Group")
pkgbase = create(PackageBase, Name="test-package", Maintainer=user) pkgbase = create(PackageBase, Name="test-package", Maintainer=user)

View file

@ -25,7 +25,7 @@ def setup():
AccountType.AccountType == "User").first() AccountType.AccountType == "User").first()
user = create(User, Username="test", Email="test@example.org", user = create(User, Username="test", Email="test@example.org",
RealName="Test User", Passwd="testPassword", RealName="Test User", Passwd="testPassword",
account_type=account_type) AccountType=account_type)
license = create(License, Name="Test License") license = create(License, Name="Test License")
pkgbase = create(PackageBase, Name="test-package", Maintainer=user) pkgbase = create(PackageBase, Name="test-package", Maintainer=user)

View file

@ -10,12 +10,12 @@ from aurweb.models.session import Session, generate_unique_sid
from aurweb.models.user import User from aurweb.models.user import User
from aurweb.testing import setup_test_db from aurweb.testing import setup_test_db
user = session = None account_type = user = session = None
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup(): def setup():
global user, session global account_type, user, session
setup_test_db("Users", "Sessions") setup_test_db("Users", "Sessions")
@ -35,7 +35,10 @@ def test_session():
def test_session_cs(): def test_session_cs():
""" Test case sensitivity of the database table. """ """ 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", SessionID="TESTSESSION",
LastUpdateTS=datetime.utcnow().timestamp()) LastUpdateTS=datetime.utcnow().timestamp())
assert session_cs.SessionID == "TESTSESSION" assert session_cs.SessionID == "TESTSESSION"