Catégorie : django

Django : créer un checkbox multiple choice avec bootstrap

Je me sers de cet habillage ici.

C’est, en gros, une « surcharge » de bootstrap avec quelques composants en plus.

Seulement j’aurais aimé avoir une sélection de plusieurs choix = cases à cocher qui soit « compatible » avec cet habillage.
Je n’aurais jamais cru que c’était aussi simple.
En fait Django a généralisé plein d’affichages HTML à tel point qu’il suffit de faire du pas à pas et de regarder ce qu’il fait pour comprendre.
Même le rendu par « item » de chaque « Renderer » est surchargeable. Donc voici le code qui prend… 10 lignes, et qui réhabille totalement le rendu des cases à cocher :

from django.forms import CheckboxSelectMultiple
from django.forms.widgets import ChoiceFieldRenderer, \
    CheckboxChoiceInput

class BootstrapChoiceFieldRenderer(ChoiceFieldRenderer):
    choice_input_class = CheckboxChoiceInput
    outer_html = '<div{id_attr}>{content}</div>'
    inner_html = '<div class="checkbox">'
                 '{choice_value}{sub_widgets}'
                 '</div>'

class CheckboxSelectMultipleBootstrap(CheckboxSelectMultiple):
    renderer = BootstrapChoiceFieldRenderer

Django : comment changer un label de fieldset dynamiquement

Voici ma classe :

class GameAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {
            'fields': (('name', 'number_of_players'), )
        }),
        (_(u'Validity'), {
            'classes': ('collapse',),
            'fields': ('date_v_debut', 'date_v_fin')
        }),
    )
    inlines = (GamePersonsInline,)

Mon problème est que je voulais changer dynamiquement 'Validity' afin de montrer soit, si le jeu n’était plus valide, soit s’il était toujours en cours.
L’idée est que s’il a une date de fin de validité, c’est que le jeu n’est plus en cours.

Voici la solution complète, extrêmement simple, et qui me permet sans avoir à déplier le groupe « dates de validité », de voir si la partie n’est plus en cours :

class GameAdmin(admin.ModelAdmin):

    def get_fieldsets(self, request, obj=None):
        fieldsets = super(GameAdmin, self).get_fieldsets(
            request, obj)
        retour = [list(x) for x in fieldsets]
        if obj.date_v_fin:
            retour[1][0] = _(u'Expired: {}').format(
                obj.date_v_fin.strftime('%Y-%m-%d %H:%M:%S'))
        else:
            retour[1][0] = _(u'Running!')
        return retour

    fieldsets = (
        (None, {
            'fields': (('name', 'number_of_players'), )
        }),
        (_(u'Validity'), {
            'classes': ('collapse',),
            'fields': ('date_v_debut', 'date_v_fin')
        }),
    )

Après c’est vrai, il y a une chose de codée en dur, le [1][0], mais je ne voyais pas d’autre solution.

Django : Not naive datetime (blah) (tzinfo is already set)

Django a une manière de gérer la date et l’heure de façon très pratique… encore faut-il la comprendre !

Il y a la date et l’heure qui prend en compte le fuseau horaire.
Imaginez qu’un type en Chine envoie une date et une heure avec le fuseau horaire de Chine sur votre serveur, via un formulaire Web.
Le plus pratique est donc d’enregistrer au format UTC = 0 décalage horaire en se souvenant que l’heure est basée sur le fuseau de Chine.
Comme ça, si un Français demande cette heure, hop, il voit que c’est pour un fuseau +1 (ou +2 selon la saison) et fait le calcul tout seul.
Génial non ?

Seule chose à laquelle il faut faire attention : Python a son package datetime, et il y a une fonction now() qui n’est pas la même que la fonction now() de Django !

Donc si vous voulez enregistrer une date « style Django », il faut faire cet import :

from django.utils.timezone import now

Ensuite, vous pourrez, par exemple, faire des différences de secondes qui fonctionneront :
def is_expired(self, nb_seconds):
    return (now()-self.date_last_update).total_seconds() > nb_seconds

(Attention au code précédent : deux choses : le now() qui doit être importé comme je l’ai dit, et total_seconds() auquel il faut faire attention, car il y a une propriété seconds qui ne fait pas du tout la même chose….)

et surtout ne pas faire from datetime import datetime

Si vous faites comme moi et faites le mauvais import, l’heure ne sera pas « recentrée » à fuseau + 0 afin que tout le monde puisse la lire !

Django : internationalisation des fichiers JavaScript

La documentation est ici, elle est très claire :

docs.djangoproject.com/translation

Enfin très claire.. jusqu’à un certain point….
Disons qu’ils oublient de préciser une chose :

Lorsqu’il faut créer la liste des messages à traduire, ils ont codé en dur deux listes à générer :

  • django
  • djangojs

Donc si vous voulez que manage.py crée correctement les fichiers JavaScript, il faut obligatoirement passer le paramètre djangojs. L’ordre à donner est donc :

manage.py makemessages -d djangojs --locale=fr_FR --locale=en_US

Django

Django aide-mémoire

Templating

Héritage du parent {% block menu %}
{{ block.super }}
{% endblock %}
Créer une variable s2_lang
puis la sortir dans le template
{% with 'x/y/js/i18n/'|add:LANGUAGE_CODE|add:'.js' as s2_lang %}
    <script src="{% static s2_lang %}"></script>
{% endwith %}

Astuces

Afficher des messages uniques à l’utilisateur
Ex: « Merci de vous être inscrit » etc.
Infrastructure des messages
Faire un import des settings, mais relatif
= pas dépendant de l’application où vous avez le fichier.
Coder « proprement » pour Django
Calculer un template « manuellement »

Aide ici
from django.template import loader
def render_sample(request):
    s = loader.get_template(
        'alerts/subject.txt'
    ).render({})
    m = loader.get_template(
        'alerts/message.txt'
    ).render({})

SQL

Requête directe

Lancer la console python (Tools » Python console),
puis taper :
from django.db import connection
cursor = connection.cursor()
cursor.execute("PRAGMA table_info(langue)")
for c in cursor.fetchall():
    print(c)


Autre exemple :
from django.db import connection
cursor = connection.cursor()
cursor.execute("delete from app_persongame where person_id=1")
cursor.fetchall()
cursor.execute("update app_persongame set state=1 where person_id=2")
cursor.fetchall()

Sauvegarde / restauration

Lancer via manage.py ([CTRL] + [ALT] + r sous PyCharm) :
Sauvegarde : dumpdata -o data.json
Restauration : loaddata data.json

Changer un mot de passe

from django.contrib.auth.models import User
u = User.objects.get(username='nomutilisateur')
u.set_password('motdepasse')
u.save()

Multilangue

Créer tous les fichiers en ignorant mes librairies tierces :
makemessages --locale fr --locale en --ignore third_party
Multilangue JavaScript : préciser le domaine
makemessages -d djangojs -i third_party --locale fr --locale en --ignore static/vendors
Ne pas oublier : compilemessages … et de redémarrer le serveur !

Multilangue : chaînes custom

Dupliquer le dossier locale hors de vos applications (= je le fais dans le dossier principal), exemple :
locale »»»» locale_unity
Modifier LOCALE_PATHS du fichier settings.py comme suit :
LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
    os.path.join(BASE_DIR, 'locale_unity'),
)
Modifiez les fichiers .po concernés.
Ne pas oublier : compilemessages … et de redémarrer le serveur !

Multilangue / JavaScript

Declaration dans urls.py

Attention, ici « app » = dossier d’une application qu’on veut traduire, moi je les appelle souvent « app ». :

from django.views.i18n import javascript_catalog, JavaScriptCatalog

js_info_dict = {
    'packages': ('app',)
}

urlpatterns = [
    url(r'^i18n/', include('django.conf.urls.i18n')),
    # blabla
]
urlpatterns += i18n_patterns(
    url(r'^jsi18n/$', javascript_catalog, js_info_dict,
        name='javascript_catalog'),
    # blabla
)

Inclure les fichiers js dans la page HTML

<script src="{% url 'javascript_catalog' %}"></script>

Ajouter aussi le fichier personnel où je mets ma fonction « raccourci » pour la traduction :
<script src="{% static 'js/globals.js' %}"></script>

Ma fonction « raccourci » pour la traduction :
function _(a) {
    return gettext(a);
}

Extraction des chaines et traduction

Très important : récupérer les chaines en précisant le domaine djangojs :
makemessages -d djangojs -i third_party --locale fr --locale en

À partir de là, deux fichiers de traduction : le classique django.po et le nouveau djangojs.po.

Exemple de code JavaScript qui fera partie des chaînes à traduire

$('#menu').empty().append(
    $('<h5 />').html(_('Waiting...'))
);

Installation

Python 3.9

virtualenv -p /usr/local/bin/python3.9 venvpython3.9
source venvpython3.9/bin/activate
pip install --upgrade pip
pip install 'django==3.0'

Installez django_markdown_app au lieu de django_markdown (car c’est le successeur, django_markdown n’est plus maintenu !)

pip install django_markdown_app
pip install django-compressor
pip install python3-openid
pip install pytz
mkdir htdocs
cd htdocs

PostgreSQL

Création d’un utilisateur + mot de passe

Être root. De là, taper :
sudo -i -u postgres
psql [nom base de donnees]
CREATE USER interro WITH PASSWORD 'i';
Très important : lui donner tous les droits sur la base :
GRANT ALL PRIVILEGES ON DATABASE "interro" to interro;
Et si l’utilisateur existe déjà :
ALTER USER interro WITH PASSWORD 'i';

Changement d’owner des tables

Se connecter à la base. Allez je mets la commande Windows :
"\Program Files\PostgreSQL\9.5\bin\psql.exe" -U postgres interro
. De là, taper (j’ai bien mis en gras le nom de l’utilisateur à qui on donne les tables) :
SELECT 'ALTER TABLE '|| schemaname || '.' || tablename ||'
OWNER TO interro;' FROM pg_tables
WHERE NOT schemaname IN ('pg_catalog', 'information_schema')
ORDER BY schemaname, tablename;

Et PostgreSQL va sortir une floppée de « ALTER TABLE...« , il suffit juste de les copier/coller dans le prompt pour les appliquer.

Vues Login / logout

– Créer l’URL de login :
url(r'^login/$', LoginView.as_view(), name='login'),

– Créer la vue LoginView
from django.contrib.auth import views

class LoginView(views.LoginView):
    template_name = 'login.html'

    def __init__(self):
        pass

– Créer l’URL de logout :
url(r'^logout/$', LogoutView.as_view(), name='logout'),

– Créer la vue LogoutView
from django.contrib.auth import views

class LogoutView(views.LogoutView):

    def __init__(self):
        self.next_page = '/'

<form action="{% url 'login' %}" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {% for field in form %}
        <label>{{ field.label }}</label>
        {% if field.errors %}
            {{ field.errors }}
        {% endif %}
        {{ field }}
    {% endfor %}
    <input type="hidden" name="next" value="{{ next }}" />
    <input class="button small" type="submit" value="Submit"/>
</form>

PyCharm et Django : comment faire une requête directe

Si, comme moi, vous voulez faire des requêtes directement et voir toutes les tables, pour les modifier manuellement, rien de plus simple, il faut juste chercher sur le Web pendant pas mal de temps. Voici la solution pour économiser de précieuses minutes :

  • Lancer la console python (Tools » Python console)
  • Taper :

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute("PRAGMA table_info(langue)")
    for c in cursor.fetchall():
        print(c)
  • Et vous obtiendrez un résultat qui pourrait ressembler à :
    (0, 'id', 'integer', 1, None, 1)
    (1, 'date_creation', 'datetime', 1, None, 0)
    (2, 'date_last_modif', 'datetime', 1, None, 0)
    (3, 'date_v_debut', 'datetime', 1, None, 0)
    (4, 'date_v_fin', 'datetime', 0, None, 0)
    (5, 'nom', 'varchar(50)', 1, None, 0)
    (6, 'locale', 'varchar(2)', 1, None, 0)
    (7, 'bidirectionnel', 'bool', 1, None, 0)
    (8, 'nom_local', 'varchar(50)', 1, None, 0)
    (9, 'active', 'bool', 1, None, 0)
  • Si vous voulez plus de détail, et que la console a des problèmes avec les accents, voici un code qui remplace les accents par un point d’interrogation :
  • Taper :

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM langue")
    for c in cursor.fetchall():
        for d in c:
            if type(d) is str:
                print(d.encode(
                    sys.stdout.encoding,
                    errors='replace'))
            else:
                print(d)
  • Et vous obtiendrez un résultat qui pourrait ressembler à :
    None
    b'Russian'
    b'ru'
    False
    b'???????'
    False

À propos

Qui suis-je ?

Dans le désordre

  • Développeur expert en Django ;
  • Développeur avancé en Python ;
  • Développeur avancé en JavaScript ;
  • Relecteur technique de plus de 17 livres sur des techniques avancées Packtpub ;
  • Développeur expert en Delphi (Pascal orienté objet) ;
  • Anglais courant ;
  • Développeur avancé en Php (aucune prestation Php, je me spécialise Python) ;
  • Développeur intermédiaire en C ;
  • Développeur en Purebasic (pour sa rapidité impressionnante) ;
  • Chef de projet Web ;

Ce qui suit n’est pas professionnel : si seul le côté pro vous intéresse, n’allez pas plus loin !
J’insiste : ne perdez pas votre temps si ma vie personnelle ne vous intéresse pas !


Une citation que j’ai parfois en tête :

Un ami est l’être dont la traîtrise provoque la plus vive surprise. Napoléon Ier

Et :

Non licet omnibus adire Corinthum

Locution Latine, littéralement « Tous ne peuvent se rendre à Corinthe », i.e., de nos jours « tous n’ont pas l’électricité jusqu’au dernier étage »… en monde anglo-saxon « not the sharpest tool in the shed ».


Qui suis-je, pas que dans le monde professionnel ?

Dans le désordre

  • Amateur de jeux de sociétés / jeux en général (+1000 sur steam) ;
  • Passionné de labyrinthes ;
  • Fan Terry Pratchett.

Mes musiques

Avec Dire Straits (qui n’a que très peu de rapport), les albums d’Extreme sont aussi les seuls albums que j’écoute encore 15 ans après et qui me donnent encore des sensations, qui suggèrent toujours des émotions et me font rêver.

Extrême (prononcez « aix-twiiime »): vraiment très peu de personnes connaissent leurs albums, à part une vieille chanson (vieille = une quinzaine d’années) qui a été un temps à la mode, qui est presque à mon sens une des plus mauvaise de leur album : « More than words ». Limite ils ont fait ça pour vendre l’album.

Ce groupe n’est pas bien, il n’est pas cool, il est pour moi vraiment la perfection.

Album #1 : Pornograffiti : le rythme Rock’n roll avec des excellents guitaristes (je n’ose pas dire « Hard Rock » parce que ça pourrait être mal interprété) et des chansons qui donnent envie de bouger est tout simplement géniallissime.

Album #2 : Yours, Mine & The Truth : Traduction (approximative) : « Vos idées, les miennes, et la vérité ». Déjà vous avez le ton de l’album. Même s’il bouge toujours très bien, il est déjà plus lent, il y a plus de mélodies douces, plus de violons, et les paroles… rien que d’y penser… bref :
– dans la section « vos idées » : l’une s’appelle « pourquoi les faiseurs de paix meurent ? » avec un speech original de Martin Luther King qui s’intègre de manière fluide dans la chanson, une autre s’appelle « le zoo politique », rien que la première chanson est incroyable : on entend le père qui parle à son fils : « tu vas faire ci et ça ok ? tu m’ENTENDS », et si on écoute bien on entend le fils qui répond « oui papa ». Si on écoute plus attentivement, on l’entend sa voix qui se modifie, il répète encore 3 – 4 fois « oui papa » et sa voix change, en s’éloignant, à la fin on entend très lointaine une voix d’homme qui dit « oui, papa »… c’est digne d’un Pink Floyd des temps modernes.
– ensuite section « mine », toutes les chansons sont intimes, et superbes, il y a des choses sympathiques comme « je suis un tragico comique » dans laquelle il raconte ses péripéties de personne super maladroite qui tente de draguer (beaucoup d’entre nous – moi y compris – ont connu à un moment ou à un autre ce genre de situation), et d’autre chansons bouleversantes : je découvrais l’album en même temps qu’une amie de 17 ans s’est tuée en mobylette, et une des chansons qui est l’incarnation de la pureté et du questionnement sain demande : « please tell me God isn’t dead… » (« dis moi que Dieu n’est pas mort »), « Look at all the lonely people loosing faith in a world full of despair please tell me God isn’t dead… » (« Regarde tous ces gens qui perdent la foi dans un monde plein de désespoir s’il te plait dis moi que Dieu n’est pas mort »), qu’on soit croyant ou pas, la voix, la question, la force de la musique, le ton qui monte, à la fin on sent que le chanteur, en souffrance, pose vraiment la question « s’il te plait dis moi que Dieu n’est pas mort ! » et à la fin il n’a pas de réponse, il se termine en disant doucement « je veux savoir… » presque comme s’il parlait à soi même… c’est juste beau.
– et pour finir la section « the truth », la vérité. A vous de la découvrir, je ne veux pas vous influencer en quoi que ce soit. Ici aussi, tout est parfait.

Attendez ce n’est pas tout ! Je n’ai pas parlé de la qualité d’écoute ! Quant à la technique du son pur… Après avoir entendu le Titanic sur des baffles d’une qualité incroyable et avoir eu vraiment l’impression que Céline Dion était à deux pas de moi, les albums d’Extrême sont les seuls autres albums qui ont fait cette impression sur ces baffles. Ça m’a donné une gifle. Ces albums sont d’une clarté sonore extraordinaire.

Si vous aimez :
– le rock’n roll,
– la guitare électrique,
– les émotions fortes,
– des albums qui ont tous un sens plus poussé que « Pour t’oublier, j’ai du t’imaginer en train de chier »,

alors courrez les acheter. Et ne cherchez pas à les télécharger illégalement ils ne sont même pas en partage !

Ignorez ce qui suit si ma vie ne vous intéresse pas !
Ne perdez pas votre temps si ma vie ne vous intéresse pas !

Note personnelles sur la MSDN

On est réellement obligé de lire toute la MSDN, parce qu’ils s’attardent sur certains détails APRÈS la doc, genre : « ah oui au fait, on a oublié : si ça fonctionne pas, c’est normal… peut-être qu’il faudrait, hein, pourquoi pas, parce que oui on dit que c’est comme ça mais pas tout le temps, hein, HEIN ».

Lis ça :
http://msdn2.microsoft.com/en-us/library/ms738547.aspx

A+