How to use password fields with Django forms on the Google App Engine

28th Feb 2009

Currently the GAE StringProperty will not render as a password field when used in a Django form.

This page will show you how to extend the StringProperty class so that it will, by using use the Django PasswordInput widget. While we are at it, we will also provide a way to set the SIZE, MAXLENGTH and CLASS attributes on the rendered INPUT field.

Here is the key code:

from google.appengine.ext import db
from django.newforms.widgets import PasswordInput, TextInput 

class StringPropertyXtr(db.StringProperty):

    def __init__(self, size=None, maxlength=None, password=False,
                 cssClass=None, **kwargs):
        self._size = size
        self._maxlength = maxlength
        self._password = password
        self._cssClass = cssClass
        super(StringPropertyXtr, self).__init__(**kwargs) 

    def get_form_field(self, **kwargs):
        defaults = {}
        attrs={}
        if self._size:
            attrs['size']=self._size
        if self._maxlength:
            attrs['maxlength']=self._maxlength
        if self._cssClass:
            attrs['class']=self._cssClass
        if self._password:
            defaults['widget']=PasswordInput(attrs)
        else:
            defaults['widget']=TextInput(attrs)
        defaults.update(kwargs)
        return super(StringPropertyXtr, self).get_form_field(**defaults)

This new property class StringPropertyXtr has all the same functionality as StringProperty, but the constructor now has four new optional parameters:

The following a complete GAE app showing how to use StringPropertyXtr in a datastore model and corresponding Django form.

import os

from google.appengine.api import users
from google.appengine.api import images
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.db import djangoforms
from django.newforms.widgets import PasswordInput, TextInput 

class StringPropertyXtr(db.StringProperty):

    def __init__(self, size=None, maxlength=None, password=False,
                 cssClass=None, **kwargs):
        self._size = size
        self._maxlength = maxlength
        self._password = password
        self._cssClass = cssClass
        super(StringPropertyXtr, self).__init__(**kwargs) 

    def get_form_field(self, **kwargs):
        defaults = {}
        attrs={}
        if self._size:
            attrs['size']=self._size
        if self._maxlength:
            attrs['maxlength']=self._maxlength
        if self._cssClass:
            attrs['class']=self._cssClass
        if self._password:
            defaults['widget']=PasswordInput(attrs)
        else:
            defaults['widget']=TextInput(attrs)
        defaults.update(kwargs)
        return super(StringPropertyXtr, self).get_form_field(**defaults)


class Usr(db.Model):
    # Use new parameters and show that existing still work.
    username = StringPropertyXtr(size=20,cssClass='inputcss',
                                 verbose_name='User name')
    password = StringPropertyXtr(password=True,maxlength=10)


class UsrForm(djangoforms.ModelForm):
    class Meta:
        model = Usr


class TestHandler(webapp.RequestHandler):

    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'test.html')
        self.response.out.write(template.render(path, {'form':UsrForm()}))

    def post(self):
        form = UsrForm(data=self.request.POST)
        self.response.out.write(template.render('test.html', {'form':form}))


application = webapp.WSGIApplication(
                                     [('/test', TestHandler)],
                                     debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

The test.html template might look like this:

<html>
  <body>
    <form method="post">
    <table>
      {{form}}
      <tr>
        <td/>
        <td>
          <button type="submit" >Submit</button>
        </td>
      </tr>
    </table>
    </form>
  </body>
</html>

This would render in the final page like this:

<html>
  <body>
    <form method="post">
    <table>
      <tr>
          <th><label for="id_username">User name:</label></th>
          <td><input id="id_username" type="text" class="inputcss" name="username" value="" size="20" /></td>
      </tr>
      <tr><th><label for="id_password">Password:</label></th>
           <td><input id="id_password" type="password" name="password" value="" maxlength="10" /></td>
      </tr>
      <tr>
        <td/>
        <td>
          <button type="submit" >Submit</button>
        </td>
      </tr>
    </table>
    </form>
  </body>
</html>

Try it out live here

Powered by Google App Engine