Archive for the ‘formularios’ Category

Change languages on-the-fly with django

This is more a reminder than a proper post, but let’s see how do we do this. I’ve been weeks wondering how the heck I could change the language on the fly in a website with the django i18n system, and the worst is that IT’S DOCUMENTED, but it’s sparse across the documentation and the source code docs (hey guys, you could write down all the documentation from time to time).

Let’s start:

To have the translation system working you need to add this middleware to your settings:

1
    django.middleware.locale.LocaleMiddleware

After that, on your urls.py file you need to include the i18n view:

1
    (r'^i18n/', include('django.conf.urls.i18n')),

That view only accepts POST requests, so if for any reason you need a GET, you will better be prepared, because you have to modify a core view, or create your own one. Ok, that view will put the url “i18n/setlang” to our service. Let’s go to the interesting part, the HTML code:

1
2
3
4
5
6
7
8
9
    <form action="/i18n/setlang/" method="post">{% csrf_token %}
      <input name="next" type="hidden" value="/" />
      <select name="language">
        {% for lang in LANGUAGES %}
          <option value="{{ lang.0 }}">{{ lang.1 }}</option>
        {% endfor %}
      </select>
      <input type="submit" value="Go" />
    </form>

This code will make a dropdown list with ALL the languages supported by django (and there are quite a few) so if you want to restrict the languages you must set up the LANGUAGES variable in your settings.py with something like this:

1
2
3
4
5
LANGUAGES = (
    ('es', 'Spanish'),
    ('en', 'English'),
    ('gl', 'Galician'),
)

Be warned, this language list is NOT translated, that means that wherever you are, that list will be always in english. To translate it you must make use of gettext directly. Do not attempt to use the translation utils from django, that will cause a circular import.

UPDATE:

Aleck gave us another way of doing the i18n selector, using links instead of a dropdown (which is very useful :) In this use case, we use list items for selecting from English and Dutch languages. This HTML code assumes you have this code in your settings.py:

1
2
3
4
LANGUAGES = (
    ('en', 'English'),
    ('nl', 'Dutch'),
)

HTML code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<li>
    <form name="setLangEnglish" action="/i18n/setlang/" method="POST">{% csrf_token %}
        <input name="next" type="hidden" value="/" />
        <input type="hidden" name="language" value="en" />
        <a href="#" onclick="document.setLangEnglish.submit();return false;">English</a>
    </form>
</li>
<li>
    <form name="setLangDutch" action="/i18n/setlang/" method="POST">{% csrf_token %}
        <input name="next" type="hidden" value="/" />
        <input type="hidden" name="language" value="nl" />
        <a href="#" onclick="document.setLangDutch.submit();return false;">Dutch</a>
    </form>                
</li>

Thank you very much Aleck!

UPDATE 2:

These days I’m updating the CSS of e-cidadania to Bootstrap 2.0 so I thought it could be a nice moment to change the language selector for something more stylish. Here is the code to use a list selector with bootstrap 2.0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<ul class="nav pull-right">
    <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown">{% trans "Language" %}<b class="caret"></b></a>
        <ul class="dropdown-menu">
	    {% for lang in LANGUAGES %}
	        <li>
                    <form name="setLang{{ lang.1}}" action="/i18n/setlang/" method="POST">{% csrf_token %}
                        <input name="next" type="hidden" value="/" />
                        <input type="hidden" name="language" value="{{ lang.0 }}" />
                        <a href="#" onclick="document.setLang{{ lang.1 }}.submit();return false;">{{ lang.1 }}</a>
                    </form>
                </li>
            {% endfor %}    
	</ul>
    </li>
</ul>

Auto rellenar formularios en django

Dejo esto aquí a modo de nota informativa y recuerdo para mis futuros problemas. Como ejemplo voy a coger el código real con el que he trabajado, ya qu es GPL.

models.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Space(models.Model):
 
    name = models.CharField(_('Name'), max_length=100, unique=True,
                            help_text=_('All lowercase. Obligatory.'))
    description = models.TextField(_('Description'))
    date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, blank=True, null=True, verbose_name=_('Author'))
 
    logo = models.ImageField(upload_to='spaces/logos',
                             verbose_name=_('Logotype'),
                             help_text=_('100px width, 75px height'))
    banner = models.ImageField(upload_to='spaces/banners',
                               verbose_name=_('Banner'),
                               help_text=_('75px height'))
    authorized_groups = models.ManyToManyField(Group,
                                            verbose_name=_('Authorized groups'))
    #theme = models.CharField(_('Theme'), m)
 
    # Modules
    mod_debate = models.BooleanField(_('Debate module'))
    mod_proposals = models.BooleanField(_('Proposals module'))
    mod_news = models.BooleanField(_('News module'))
    mod_cal = models.BooleanField(_('Calendar module'))
    mod_docs = models.BooleanField(_('Documents module'))
 
    class Meta:
        ordering = ['name']
        verbose_name_plural = _('Spaces')
 
    def __unicode__(self):
        return self.name

Los campos que desees rellenar automáticamente en la vista, deben ir obligatoriamente marcados como que se pueden dejar en blanco, mediante blank=True o null=True o ambos si es necesario (este caso).

Para no rompernos demasiado la cabeza, haremos uso del ModelForms de django, y de esa forma dejaremos un código limpio.

forms.py

1
2
3
4
5
from e_cidadania.apps.spaces.models import Space
 
class SpaceForm(ModelForm):
    class Meta:
        model = Space

Una vez hecho esto toca generar la vista. Desconozco si se puede generar una vista manipulable mediante vistas genéricas, pero me da que no. No os preocupéis, el código necesario para la vista es bastante intuitivo y muy cortito.

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@permission_required('Space.add_space')
def create_space(request):
 
    space = Space()
    form = SpaceForm(request.POST or None, request.FILES or None, instance=space)
 
    if request.POST:
        form_uncommited = form.save(commit=False)
        form_uncommited.author = request.user
        if form.is_valid():
            form_uncommited.save()
            space = form.name
            return render_to_response(space)
 
    return render_to_response('spaces/add.html',
                              {'form': form},
                              context_instance=RequestContext(request))

El código es bastante autoexplicativo, pero de todas formas lo expondré. Cuando se crea un espacio nuevo se genera una instancia de Space para indicarle al formulario a dónde pertenece, aunque no es necesario, ya que lo detecta automáticamente. Además se genera un formulario vacío o lleno dependiendo de la situación.

Cuando se ejecuta la vista, se envía un request, si no era de tipo POST (cuando enviamos un formulario) request.POST y request.FILES no existen por lo tanto les otorga el valor None y con ello un formulario vacío.

Si no hay POST, la cláusula “if” no se ejecuta y al usuario se le presenta un formulario vacío listo para rellenar. Ahora viene lo bueno, ¿qué ocurre cuando rellenamos el formulario y lo enviamos? Hay campos como el de “autor” o el de fecha que deberían ser automáticos. Acordaros de que aunque no deben ser campos visibles al usuario, sí que tienen que estar en la plantilla como {{ is_hidden }}. De lo contrario estaréis enviando un formulario incompleto cuando hagáis el POST (por mucho que lo rellenéis más tarde en la vista).

El campo de fecha se rellenará sólo, así que por eso no te preocupes, el inconveniente viene a la hora de guardar el autor. Debemos guardar el formulario pero sin enviarlo, esto lo hacemos con un save() enviando el parámetro “commit=False“, para luego añadirle o modificarle los valores que nosotros necesitemos. Tras ello, pasamos la validación (si hicésemos la validación antes no la pasaría) y guardamos definitivamente. Después de eso detectamos el nombre del espacio que hemos creado y renviamos al usuario al mismo.