models.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.management.models
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~
  5. This module contains all management related models.
  6. :copyright: (c) 2014 by the FlaskBB Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. from wtforms import (TextField, IntegerField, FloatField, BooleanField,
  10. SelectField, SelectMultipleField, validators)
  11. from flask_wtf import Form
  12. from flaskbb._compat import max_integer, text_type, iteritems
  13. from flaskbb.extensions import db, cache
  14. from flaskbb.utils.database import CRUDMixin
  15. class SettingsGroup(db.Model, CRUDMixin):
  16. __tablename__ = "settingsgroup"
  17. key = db.Column(db.String(255), primary_key=True)
  18. name = db.Column(db.String(255), nullable=False)
  19. description = db.Column(db.Text, nullable=False)
  20. settings = db.relationship("Setting", lazy="dynamic", backref="group",
  21. cascade="all, delete-orphan")
  22. def __repr__(self):
  23. return "<{} {}>".format(self.__class__.__name__, self.key)
  24. class Setting(db.Model, CRUDMixin):
  25. __tablename__ = "settings"
  26. key = db.Column(db.String(255), primary_key=True)
  27. value = db.Column(db.PickleType, nullable=False)
  28. settingsgroup = db.Column(db.String,
  29. db.ForeignKey('settingsgroup.key',
  30. use_alter=True,
  31. name="fk_settingsgroup"),
  32. nullable=False)
  33. # The name (displayed in the form)
  34. name = db.Column(db.String(200), nullable=False)
  35. # The description (displayed in the form)
  36. description = db.Column(db.Text, nullable=False)
  37. # Available types: string, integer, float, boolean, select, selectmultiple
  38. value_type = db.Column(db.String(20), nullable=False)
  39. # Extra attributes like, validation things (min, max length...)
  40. # For Select*Fields required: choices
  41. extra = db.Column(db.PickleType)
  42. @classmethod
  43. def get_form(cls, group):
  44. """Returns a Form for all settings found in :class:`SettingsGroup`.
  45. :param group: The settingsgroup name. It is used to get the settings
  46. which are in the specified group.
  47. """
  48. class SettingsForm(Form):
  49. pass
  50. # now parse the settings in this group
  51. for setting in group.settings:
  52. field_validators = []
  53. if setting.value_type in ("integer", "float"):
  54. validator_class = validators.NumberRange
  55. elif setting.value_type == "string":
  56. validator_class = validators.Length
  57. # generate the validators
  58. if "min" in setting.extra:
  59. # Min number validator
  60. field_validators.append(
  61. validator_class(min=setting.extra["min"])
  62. )
  63. if "max" in setting.extra:
  64. # Max number validator
  65. field_validators.append(
  66. validator_class(max=setting.extra["max"])
  67. )
  68. # Generate the fields based on value_type
  69. # IntegerField
  70. if setting.value_type == "integer":
  71. setattr(
  72. SettingsForm, setting.key,
  73. IntegerField(setting.name, validators=field_validators,
  74. description=setting.description)
  75. )
  76. # FloatField
  77. elif setting.value_type == "float":
  78. setattr(
  79. SettingsForm, setting.key,
  80. FloatField(setting.name, validators=field_validators,
  81. description=setting.description)
  82. )
  83. # TextField
  84. elif setting.value_type == "string":
  85. setattr(
  86. SettingsForm, setting.key,
  87. TextField(setting.name, validators=field_validators,
  88. description=setting.description)
  89. )
  90. # SelectMultipleField
  91. elif setting.value_type == "selectmultiple":
  92. # if no coerce is found, it will fallback to unicode
  93. if "coerce" in setting.extra:
  94. coerce_to = setting.extra['coerce']
  95. else:
  96. coerce_to = text_type
  97. setattr(
  98. SettingsForm, setting.key,
  99. SelectMultipleField(
  100. setting.name,
  101. choices=setting.extra['choices'](),
  102. coerce=coerce_to,
  103. description=setting.description
  104. )
  105. )
  106. # SelectField
  107. elif setting.value_type == "select":
  108. # if no coerce is found, it will fallback to unicode
  109. if "coerce" in setting.extra:
  110. coerce_to = setting.extra['coerce']
  111. else:
  112. coerce_to = text_type
  113. setattr(
  114. SettingsForm, setting.key,
  115. SelectField(
  116. setting.name,
  117. coerce=coerce_to,
  118. choices=setting.extra['choices'](),
  119. description=setting.description)
  120. )
  121. # BooleanField
  122. elif setting.value_type == "boolean":
  123. setattr(
  124. SettingsForm, setting.key,
  125. BooleanField(setting.name, description=setting.description)
  126. )
  127. return SettingsForm
  128. @classmethod
  129. def get_all(cls):
  130. return cls.query.all()
  131. @classmethod
  132. def update(cls, settings, app=None):
  133. """Updates the cache and stores the changes in the
  134. database.
  135. :param settings: A dictionary with setting items.
  136. """
  137. # update the database
  138. for key, value in iteritems(settings):
  139. setting = cls.query.filter(Setting.key == key.lower()).first()
  140. setting.value = value
  141. db.session.add(setting)
  142. db.session.commit()
  143. cls.invalidate_cache()
  144. @classmethod
  145. def get_settings(cls, from_group=None):
  146. """This will return all settings with the key as the key for the dict
  147. and the values are packed again in a dict which contains
  148. the remaining attributes.
  149. :param from_group: Optionally - Returns only the settings from a group.
  150. """
  151. result = None
  152. if from_group is not None:
  153. result = from_group.settings
  154. else:
  155. result = cls.query.all()
  156. settings = {}
  157. for setting in result:
  158. settings[setting.key] = {
  159. 'name': setting.name,
  160. 'description': setting.description,
  161. 'value': setting.value,
  162. 'value_type': setting.value_type,
  163. 'extra': setting.extra
  164. }
  165. return settings
  166. @classmethod
  167. @cache.memoize(timeout=max_integer)
  168. def as_dict(cls, from_group=None, upper=True):
  169. """Returns all settings as a dict. This method is cached. If you want
  170. to invalidate the cache, simply execute ``self.invalidate_cache()``.
  171. :param from_group: Returns only the settings from the group as a dict.
  172. :param upper: If upper is ``True``, the key will use upper-case
  173. letters. Defaults to ``False``.
  174. """
  175. settings = {}
  176. result = None
  177. if from_group is not None:
  178. result = SettingsGroup.query.filter_by(key=from_group).\
  179. first_or_404()
  180. result = result.settings
  181. else:
  182. print(Setting.query)
  183. result = cls.query.all()
  184. for setting in result:
  185. if upper:
  186. setting_key = setting.key.upper()
  187. else:
  188. setting_key = setting.key
  189. settings[setting_key] = setting.value
  190. return settings
  191. @classmethod
  192. def invalidate_cache(cls):
  193. """Invalidates this objects cached metadata."""
  194. cache.delete_memoized(cls.as_dict, cls)