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
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)

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.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])

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. """
__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 "<AccountType(ID='%s', AccountType='%s')>" % (
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.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])

View file

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

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):
self.Name = Name
mapper(DependencyType, DependencyTypes)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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])

View file

@ -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])

View file

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

View file

@ -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])

View file

@ -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
])

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):
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.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)

View file

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

View file

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

View file

@ -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 "<User(ID='%s', AccountType='%s', Username='%s')>" % (
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()
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():

View file

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

View file

@ -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()

View file

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

View file

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

View file

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