1
0
forked from ScoDoc/ScoDoc

ScoDocModel super-classe

This commit is contained in:
Emmanuel Viennet 2023-12-22 15:21:07 +01:00
parent 5e39b3ae44
commit 91bca7eed9
4 changed files with 41 additions and 14 deletions

View File

@ -58,7 +58,7 @@ def invalid_user_name(user_name: str) -> bool:
) )
class User(UserMixin, db.Model, ScoDocModel): class User(UserMixin, ScoDocModel):
"""ScoDoc users, handled by Flask / SQLAlchemy""" """ScoDoc users, handled by Flask / SQLAlchemy"""
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

View File

@ -23,8 +23,17 @@ convention = {
metadata_obj = sqlalchemy.MetaData(naming_convention=convention) metadata_obj = sqlalchemy.MetaData(naming_convention=convention)
class ScoDocModel: class ScoDocModel(db.Model):
"Mixin class for our models. Add somme useful methods for editing, cloning, etc." """Superclass for our models. Add some useful methods for editing, cloning, etc.
- clone() : clone object and add copy to session, do not commit.
- create_from_dict() : create instance from given dict, applying conversions.
- convert_dict_fields() : convert dict values, called before instance creation.
By default, do nothing.
- from_dict() : update object using data from dict. data is first converted.
- edit() : update from wtf form.
"""
__abstract__ = True # declare an abstract class for SQLAlchemy
def clone(self, not_copying=()): def clone(self, not_copying=()):
"""Clone, not copying the given attrs """Clone, not copying the given attrs
@ -40,13 +49,19 @@ class ScoDocModel:
return copy return copy
@classmethod @classmethod
def create_from_dict(cls, data: dict): def create_from_dict(cls, data: dict) -> "ScoDocModel":
"""Create a new instance of the model with attributes given in dict. """Create a new instance of the model with attributes given in dict.
The instance is added to the session (but not flushed nor committed). The instance is added to the session (but not flushed nor committed).
Use only relevant arributes for the given model and ignore others. Use only relevant arributes for the given model and ignore others.
""" """
if data:
args = cls.convert_dict_fields(cls.filter_model_attributes(data)) args = cls.convert_dict_fields(cls.filter_model_attributes(data))
if args:
obj = cls(**args) obj = cls(**args)
else:
obj = cls()
else:
obj = cls()
db.session.add(obj) db.session.add(obj)
return obj return obj
@ -79,15 +94,27 @@ class ScoDocModel:
# virtual, by default, do nothing # virtual, by default, do nothing
return args return args
def from_dict(self, args: dict, excluded: set[str] | None = None): def from_dict(self, args: dict, excluded: set[str] | None = None) -> bool:
"Update object's fields given in dict. Add to session but don't commit." """Update object's fields given in dict. Add to session but don't commit.
True if modification.
"""
args_dict = self.convert_dict_fields( args_dict = self.convert_dict_fields(
self.filter_model_attributes(args, excluded=excluded) self.filter_model_attributes(args, excluded=excluded)
) )
modified = False
for key, value in args_dict.items(): for key, value in args_dict.items():
if hasattr(self, key): if hasattr(self, key) and value != getattr(self, key):
setattr(self, key, value) setattr(self, key, value)
modified = True
db.session.add(self) db.session.add(self)
return modified
def edit_from_form(self, form) -> bool:
"""Generic edit method for updating model instance.
True if modification.
"""
args = {field.name: field.data for field in form}
return self.from_dict(args)
from app.models.absences import Absence, AbsenceNotification, BilletAbsence from app.models.absences import Absence, AbsenceNotification, BilletAbsence

View File

@ -23,7 +23,7 @@ from app.scodoc.sco_exceptions import ScoInvalidParamError, ScoValueError
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
class Identite(db.Model, models.ScoDocModel): class Identite(models.ScoDocModel):
"""étudiant""" """étudiant"""
__tablename__ = "identite" __tablename__ = "identite"
@ -798,7 +798,7 @@ def pivot_year(y) -> int:
return y return y
class Adresse(db.Model, models.ScoDocModel): class Adresse(models.ScoDocModel):
"""Adresse d'un étudiant """Adresse d'un étudiant
(le modèle permet plusieurs adresses, mais l'UI n'en gère qu'une seule) (le modèle permet plusieurs adresses, mais l'UI n'en gère qu'une seule)
""" """
@ -834,7 +834,7 @@ class Adresse(db.Model, models.ScoDocModel):
return e return e
class Admission(db.Model, models.ScoDocModel): class Admission(models.ScoDocModel):
"""Informations liées à l'admission d'un étudiant""" """Informations liées à l'admission d'un étudiant"""
__tablename__ = "admissions" __tablename__ = "admissions"

View File

@ -19,7 +19,7 @@ from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
class Partition(db.Model, ScoDocModel): class Partition(ScoDocModel):
"""Partition: découpage d'une promotion en groupes""" """Partition: découpage d'une promotion en groupes"""
__table_args__ = (db.UniqueConstraint("formsemestre_id", "partition_name"),) __table_args__ = (db.UniqueConstraint("formsemestre_id", "partition_name"),)
@ -205,7 +205,7 @@ class Partition(db.Model, ScoDocModel):
return group return group
class GroupDescr(db.Model, ScoDocModel): class GroupDescr(ScoDocModel):
"""Description d'un groupe d'une partition""" """Description d'un groupe d'une partition"""
__tablename__ = "group_descr" __tablename__ = "group_descr"