FormAlchemy example with custom validator, renderers, forms - Pylons app


/ Published in: Python
Save to your folder(s)

This is based on FormEncode 1.2 + two patches you get here:
http://code.google.com/p/formalchemy/issues/detail?id=83
and here:
http://code.google.com/p/formalchemy/issues/detail?id=84
(at this point, they might be included in the official release).


Copy this code and paste it in your HTML
  1. ### My App name = Vigilia
  2.  
  3. ## in vigilia/forms/__init__.py
  4. class FieldSet(forms.FieldSet):
  5. def _render(self, **kwargs):
  6. return render('/fieldset.mako',
  7. extra_vars=kwargs)
  8. def _render_readonly(self, **kwargs):
  9. return render('/fieldset_readonly.mako',
  10. extra_vars=kwargs)
  11.  
  12. def insert_after(self, after_what, field):
  13. """Insert a field to be rendered after a given field"""
  14. idx = self._render_fields._list.index(after_what)
  15. if idx is not None:
  16. self.insert_at_index(idx + 1, field)
  17. else:
  18. raise ValueError('No such field in render_fields: %s' % after_what)
  19.  
  20. def insert_at_index(self, idx, field):
  21. """Insert a field to be rendered before a given field"""
  22. if field.key in self._render_fields._list:
  23. self._render_fields._list.remove(field.key)
  24. self._render_fields._list.insert(idx, field.key)
  25. self._render_fields[field.key] = field
  26.  
  27.  
  28. class RadioSetRenderer(fields.RadioSet):
  29. widget = staticmethod(h.radio)
  30. def render(self, options, **kwargs):
  31. self.radios = []
  32. self.options = opts = options
  33. for i, (choice_name, choice_value) in enumerate(opts):
  34. choice_id = '%s_%i' % (self.name, i)
  35. radio = self.widget(self.name, choice_name, id=choice_id,
  36. checked=self._is_checked(choice_value), **kwargs)
  37. label = h.make_tag('label', c=radio + ' %s' % choice_value)
  38. self.radios.append(label)
  39. return h.make_tag('div', c=h.make_tag("br").join(self.radios))
  40.  
  41. class CheckboxRenderer(RadioSetRenderer):
  42. widget = staticmethod(h.checkbox)
  43.  
  44.  
  45. class EmailSmsRenderer(fields.TextFieldRenderer):
  46. pass
  47.  
  48. class PermissionField(fields.Field):
  49. is_collection = False
  50. is_composite_foreign_key = False
  51.  
  52. class PermissionsRenderer(fields.SelectFieldRenderer):
  53. def render(self, **kwargs):
  54. return h.select(self.name, [p.permission for p in self.field.model.permissions],
  55. model.permissions_list,
  56. size=len(model.permissions_list),
  57. id=self.name, multiple=True)
  58. def deserialize(self):
  59. # This simple calculation makes sure we don't delete rows that
  60. # haven't changed. We only add rows that were actually added,
  61. # and remove the rows that were actually deleted from the last
  62. # saved records.
  63. u = self.field.model
  64.  
  65. p1 = set([perm.permission for perm in u.permissions])
  66. p2 = set(self._params.getall(self.name))
  67. p_rem = list(p1.difference(p2))
  68. p_add = list(p2.difference(p1))
  69.  
  70. # Remove unwanted permissions..
  71. u.permissions = [perm for perm in u.permissions
  72. if perm.permission not in p_rem]
  73. # Add new permissions
  74. for x in p_add:
  75. u.permissions.append(model.Permission(x))
  76.  
  77. return None
  78.  
  79.  
  80. def password_validator(value, field):
  81. if field.parent.passwd1.value != value:
  82. raise validators.ValidationError('Les deux mots de passes ne concordent pas')
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89. ## in vigilia/model/___init___.py
  90.  
  91.  
  92. # Permissions definitions
  93. permissions_list = [
  94. ('admin', u'Administration globale'),
  95. ('moderation', u'Modération des commentaires'),
  96. ('posts_admin', u'Administration des posts'),
  97. ('posts_post', u'Poster un événement'),
  98. ('events_admin', u'Gestion des Activités'),
  99. ('logs_view', u'Visionnement des logs'),
  100. ]
  101.  
  102. # Users
  103. users_t = Table('users', metadata,
  104. Column('id', Integer, primary_key=True),
  105. Column('username', String(25)),
  106. Column('password', String(50)),
  107. Column('firstname', Unicode(80)),
  108. Column('lastname', Unicode(80)),
  109. Column('gender', String(5), default='M'),
  110. Column('email', Unicode(255)),
  111. Column('email_sms', Unicode(150)),
  112. Column('photo', Unicode(255)), # Lien relatif à une variable
  113. # de configuration, vers l'image
  114. Column('telephone', Unicode(100)),
  115. Column('dob', Date), # Date of birth - Sans garder l'année
  116. Column('pref_email_new_post', Boolean),
  117. Column('pref_email_updt_post', Boolean),
  118. Column('pref_email_sms_post', Boolean), # Only when asked for
  119. Column('pref_email_new_event', Boolean),
  120. Column('pref_email_updt_event', Boolean),
  121. )
  122.  
  123.  
  124. class User(object):
  125. @property
  126. def fullname(self):
  127. """Create the full-name representation of the user."""
  128. if not self.firstname:
  129. return unicode(self.username)
  130. return self.firstname + \
  131. (' %s' % self.lastname if self.lastname else '')
  132.  
  133. def __str__(self):
  134. return self.fullname
  135.  
  136. def __repr__(self):
  137. return "<User: %s>" % self.username
  138.  
  139. def __cmp__(self, other):
  140. if self.firstname is None or other.firstname is None:
  141. return 0
  142. n1 = self.firstname + self.lastname
  143. n2 = other.firstname + other.lastname
  144. return cmp(n1, n2)
  145.  
  146. def has_perm(self, *args):
  147. """Checks if user has certain permissions"""
  148. return has_perm(permissions=[p.permission for p in self.permissions],
  149. *args)
  150.  
  151.  
  152.  
  153. mapper(User, users_t, {
  154. 'post_answers': relation(PostAnswer, lazy=True, backref='answer_user'),
  155. 'recipient_to': relation(Recipient, backref='user'),
  156. 'last_viewed': relation(LastViewed, backref='user'),
  157. 'permissions': relation(Permission),
  158. 'openids': relation(OpenID, backref='user'),
  159. })
  160.  
  161.  
  162.  
  163.  
  164. ## in vigilia/controller/users.py
  165.  
  166. from vigilia.lib.base import *
  167.  
  168. log = logging.getLogger(__name__)
  169.  
  170. def gen_fieldset(mdl):
  171. fs = forms.FieldSet(mdl)
  172. fs.add(forms.Field('passwd1'))
  173. fs.add(forms.Field('passwd2'))
  174. fs.add(forms.PermissionField(name='permissions'))
  175. fs.add(forms.Field('alert', value='').label(u'E-mail notification')\
  176. .checkbox([('X', u'Envoyer un courriel avec ces informations '\
  177. 'au nouvel usager')])\
  178. .with_renderer(forms.CheckboxRenderer))
  179. inc = [fs.username.label(u"Nom d'usager"),
  180. fs.passwd1.password().label(u'Mot de passe'),
  181. fs.passwd2.password().label(u'Répétez le mot de passe').validate(forms.password_validator),
  182. fs.gender.label(u'Genre').radio([('M', 'M'), ('F', 'F')]).with_renderer(forms.RadioSetRenderer),
  183. fs.firstname.label(u'Prénom'),
  184. fs.lastname.label(u'Nom de famille'),
  185. fs.email.label(u'Courriel'),
  186. fs.permissions.label(u'Permissions').with_renderer(forms.PermissionsRenderer),
  187. fs.groups.label(u'Groupes'),
  188. fs.email_sms.label(u'Passerelle SMS (courriel)').with_renderer(forms.EmailSmsRenderer),
  189. fs.dob.label(u'Date de naissance').with_renderer(forms.DateFieldRendererFr),
  190. ]
  191. fs.configure(include=inc)
  192. return fs
  193.  
  194. class UsersController(BaseController):
  195.  
  196. @has_perm('admin')
  197. def index(self):
  198. c.list = model.User.query().order_by('firstname, lastname').all()
  199.  
  200. return render('/users/index.html')
  201.  
  202.  
  203. @has_perm('admin')
  204. def edit(self, id):
  205. # New or modify
  206. if id:
  207. c.usr = model.User.query.get(id)
  208. else:
  209. c.usr = model.User
  210.  
  211. c.permissions_list = model.permissions_list
  212.  
  213. fs = gen_fieldset(c.usr)
  214. fs = fs.bind(c.usr, data=request.POST or None)
  215.  
  216. if not id:
  217. # Require password if it's first adding..
  218. fs.insert_after('passwd2', fs.alert)
  219. fs.configure(options=[fs.passwd1.required()])
  220.  
  221. if request.POST and fs.validate():
  222. fs.sync()
  223. if fs.passwd1.value:
  224. fs.model.password = fs.passwd1.value
  225. push_flash(INFO, u"Le mot de passe de l'usager a été modifié")
  226. if fs.alert.value:
  227. ### TODO: send a notification e-mail here
  228. push_flash(INFO, u"Un courriel de notification a été envoyé")
  229. meta.Session.commit()
  230. push_flash(SUCCESS, u"Usager sauvegardé avec succès")
  231. redirect_to(controller='users', action='index')
  232.  
  233. c.fieldset = fs
  234. return render('/users/edit.html')
  235.  
  236.  
  237. ## in vigilia/lib/base.py
  238.  
  239. rom vigilia import forms

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.