models.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import base64
  2. try:
  3. import cPickle as pickle
  4. except ImportError:
  5. import pickle
  6. from wtforms import (TextField, IntegerField, BooleanField, SelectField,
  7. FloatField, validators)
  8. from flask.ext.wtf import Form
  9. from flaskbb.extensions import db
  10. def normalize_to(value, value_type, reverse=False):
  11. """Converts a value to a specified value type.
  12. Available value types are: string, integer, boolean and array.
  13. A boolean type is handled as 0 for false and 1 for true.
  14. :param value: The value which should be converted.
  15. :param value_type: The value_type.
  16. :param reverse: If the value should be converted back
  17. """
  18. if reverse:
  19. if value_type == 'array':
  20. return value.split(',')
  21. if value_type == 'integer':
  22. return int(value)
  23. if value_type == 'float':
  24. return float(value)
  25. if value_type == 'boolean':
  26. return value == "1"
  27. # text
  28. return value
  29. else:
  30. if value_type == 'array':
  31. return ",".join(value)
  32. if value_type == 'integer':
  33. return int(value)
  34. if value_type == 'float':
  35. return float(value)
  36. if value_type == 'boolean':
  37. return 1 if value else 0
  38. # text
  39. return value
  40. class SettingsGroup(db.Model):
  41. __tablename__ = "settingsgroup"
  42. key = db.Column(db.String, primary_key=True)
  43. name = db.Column(db.String, nullable=False)
  44. description = db.Column(db.String, nullable=False)
  45. settings = db.relationship("Setting", lazy="dynamic", backref="group")
  46. def save(self):
  47. db.session.add(self)
  48. db.session.commit()
  49. class Setting(db.Model):
  50. __tablename__ = "settings"
  51. key = db.Column(db.String, primary_key=True)
  52. _value = db.Column("value", db.String, nullable=False)
  53. settingsgroup = db.Column(db.String,
  54. db.ForeignKey('settingsgroup.key',
  55. use_alter=True,
  56. name="fk_settingsgroup"),
  57. nullable=False)
  58. # The name (displayed in the form)
  59. name = db.Column(db.String, nullable=False)
  60. # The description (displayed in the form)
  61. description = db.Column(db.String, nullable=False)
  62. # Available types: string, integer, boolean, array, float
  63. value_type = db.Column(db.String, nullable=False)
  64. # Available types: text, number, choice, yesno
  65. # They are used in the form creation process
  66. input_type = db.Column(db.String, nullable=False)
  67. # Extra attributes like, validation things (min, max length...)
  68. _extra = db.Column("extra", db.String)
  69. # Properties
  70. @property
  71. def value(self):
  72. return normalize_to(self._value, self.value_type)
  73. @value.setter
  74. def value(self, value):
  75. self._value = normalize_to(value, self.value_type, reverse=True)
  76. @property
  77. def extra(self):
  78. return pickle.loads(base64.decodestring(self._extra))
  79. @extra.setter
  80. def extra(self, extra):
  81. self._extra = base64.encodestring(
  82. pickle.dumps((extra), pickle.HIGHEST_PROTOCOL)
  83. )
  84. @classmethod
  85. def get_form(cls, group):
  86. """Returns a Form for all settings found in :class:`SettingsGroup`.
  87. :param group: The settingsgroup name. It is used to get the settings
  88. which are in the specified group. Aborts with 404 if the
  89. group is found.
  90. """
  91. settings = SettingsGroup.query.filter_by(key=group).first_or_404()
  92. class SettingsForm(Form):
  93. pass
  94. # now parse that shit
  95. for setting in settings.settings:
  96. field_validators = []
  97. # generate the validators
  98. # TODO: Do this in another function
  99. if "min" in setting.extra:
  100. # Min number validator
  101. if setting.value_type in ("integer", "float"):
  102. field_validators.append(
  103. validators.NumberRange(min=setting.extra["min"])
  104. )
  105. # Min text length validator
  106. elif setting.value_type in ("string", "array"):
  107. field_validators.append(
  108. validators.Length(min=setting.extra["min"])
  109. )
  110. if "max" in setting.extra:
  111. # Max number validator
  112. if setting.value_type in ("integer", "float"):
  113. field_validators.append(
  114. validators.NumberRange(max=setting.extra["max"])
  115. )
  116. # Max text length validator
  117. elif setting.value_type in ("string", "array"):
  118. field_validators.append(
  119. validators.Length(max=setting.extra["max"])
  120. )
  121. # Generate the fields based on input_type and value_type
  122. if setting.input_type == "number":
  123. # IntegerField
  124. if setting.value_type == "integer":
  125. setattr(
  126. SettingsForm, setting.key,
  127. IntegerField(setting.name, validators=field_validators,
  128. description=setting.description)
  129. )
  130. # FloatField
  131. elif setting.value_type == "float":
  132. setattr(
  133. SettingsForm, setting.key,
  134. FloatField(setting.name, validators=field_validators,
  135. description=setting.description)
  136. )
  137. # TextField
  138. if setting.input_type == "text":
  139. setattr(
  140. SettingsForm, setting.key,
  141. TextField(setting.name, validators=field_validators,
  142. description=setting.description)
  143. )
  144. # SelectField
  145. if setting.input_type == "choice" and "choices" in setting.extra:
  146. setattr(
  147. SettingsForm, setting.key,
  148. SelectField(setting.name, choices=setting.extra['choices'],
  149. description=setting.description)
  150. )
  151. # BooleanField
  152. if setting.input_type == "yesno":
  153. setattr(
  154. SettingsForm, setting.key,
  155. BooleanField(setting.name, description=setting.description)
  156. )
  157. return SettingsForm
  158. @classmethod
  159. def get_all(cls):
  160. return cls.query.all()
  161. @classmethod
  162. def update(cls, settings, app=None):
  163. """Updates the current_app's config and stores the changes in the
  164. database.
  165. :param config: A dictionary with configuration items.
  166. """
  167. updated_settings = {}
  168. for key, value in settings.iteritems():
  169. setting = cls.query.filter(Setting.key == key.lower()).first()
  170. setting.value = value
  171. updated_settings[setting.key.upper()] = setting.value
  172. db.session.add(setting)
  173. db.session.commit()
  174. if app is not None:
  175. app.config.update(updated_settings)
  176. @classmethod
  177. def as_dict(cls, from_group=None, upper=False):
  178. """Returns the settings key and value as a dict.
  179. :param from_group: Returns only the settings from the group as a dict.
  180. :param upper: If upper is ``True``, the key will use upper-case
  181. letters. Defaults to ``False``.
  182. """
  183. settings = {}
  184. result = None
  185. if from_group is not None:
  186. result = SettingsGroup.query.filter_by(key=from_group).\
  187. first_or_404()
  188. result = result.settings
  189. else:
  190. result = cls.query.all()
  191. for setting in result:
  192. if upper:
  193. settings[setting.key.upper()] = setting.value
  194. else:
  195. settings[setting.key] = setting.value
  196. return settings
  197. def save(self):
  198. db.session.add(self)
  199. db.session.commit()