Catégorie : développement Internet

Go langage

Mémo go

Uniquement des notes sur ce qui est « original » dans le langage « go », par rapport aux autres langages.

Variables

Déclaration

Par rapport à tous les autres langages, il faut le faire à l’envers :
var x int
Et pour un tableau : var [nom] [[longueur]][type], exemple :
var s [3]int

Initialisation avec des valeurs

Pas d’explication, un exemple direct suffit : arr := [2]int{2, 4}

Inférence de type

Une déclaration complète d’une variable serait var i int = 12
Une déclaration ultra simplifiée acceptable est i := 12 et dans ce cas, Go va tenter de deviner le type (« type inference »).

Comparaison de tableaux

La déclaration de variables se fait toujours en précisant la longueur, et au moment de comparer, il faut que les tableaux aient exactement la même longueur, sinon on aura une erreur. C’est pour cela que les tableaux sont moins utilisés que le type slice.

Comparaison = avoir des types identiques

Go n’essaie jamais de transformer les variables en booléens pour pouvoir les comparer, c’est à nous transformer une variable ou l’autre de manière à ce qu’elles aient toujours le même type.

Création d’une variable dans un « if »

Exemple direct : if b:= a / 2; b > 5 {...} else {...}
La variable b n’existera que dans le bloc if / else.

Boucle for / range

s := "Bonjour"
for k, v := range s {
    fmt.Println(k, v, string(v))
}

k correspond à l’index, et n’est pas toujours incrémenté que de 1, selon l’encodage. C’est la méthode à utiliser pour parcourir une chaîne en utf8. Car via un classique for, Go parcourra la chaîne « à la » C, c’est à dire que un caractère = un octet (ce qui n’est pas le cas en utf8).

Switch / case

Par défaut, les « break » sont implicites, pas besoin de les mettre. Si on veut exécuter l’instruction du « case » suivant (= comme en C ou JavaScript), il faut le dire explicitement via « fallthrough« .

Il est possible de créer une variable qui ne vivra que le temps du switch / case, exemple :

switch l := len(word); word {
    ...blabla...
}

Contrairement aux autres langages, il ne faut pas mettre plusieurs case d’affilée pour gérer plusieurs cas au même endroit, il suffit de les séparer par une virgule, exemple pour continuer le précédent :

switch l := len(word); word {
case 1 < l && l < 10, word == "ok":
    fmt.Println("Soit mot : 1 < longueur < 10, soit il vaut 'ok'")
}

Fonctions

Appels

Tous les appels de fonction sont par valeur. Jamais par référence. Nombre, booléen, ainsi que les tableaux.

Fonctions en tant que variable

Exemple direct :
func myFunc() {
    f := func(a int) int {
        return a + 1
    }
    fmt.Println(f(1))
}

Fonctions en tant que paramètres

Exemple direct d’un paramètre qui doit être « une fonction qui prend un entier en tant que paramètre et renvoie un entier » :
func myFunc(a int, f func(int) int) {
    fmt.Println(f(a))
}

Pointeurs

Appels

Conversions

Astuces classiques

Site officiel

Packages

JavaScript : comment surcharger console.log()

J’avais besoin de décaler mes log pour voir rapidement si j’étais, ou pas, à l’intérieur d’une fonction.

Eh oui, après plusieurs années, même si on peut faire des points d’arrêt, JavaScript est là pour répondre à des événements utilisateurs et autres… donc il ne faut souvent pas arrêter pendant l’exécution ou la visualisation d’une page Web… on n’a toujours pas d’autre choix que de faire le bon console.log() des familles qui fait que le développement est extrêmement ralenti…

Je me suis donc crée les fonctions log_in(), log() et log_out().
Ainsi, quand j’entre dans une fonction, j’appelle log_in(), et tous mes log() dans cette fonction seront décalés, ce qui fait que je vois immédiatement où sont appelés mes logs ! Ensuite, ne pas oublier de faire un log_out().

let logLevel = 0;
let log = function () {
    let args = Array.from(arguments);
    if (logLevel > 0) {
        args = [Array(logLevel).fill(' ').join('')].concat(args);
    }
    console.log.apply(null, args);
};
let log_in = function () {
    log.apply(null, arguments);
    logLevel++;
}, log_out = function (s) {
    if (logLevel>0) {
        logLevel--;
    }
    log.apply(null, arguments);
};

curl

Curl hints / aide

curl -v -H "Host:www.djangoproject.com" https://www.olivierpons.fr/

IUT : projet JavaScript – jQuery à rendre le 12 avril minuit au plus tard

A rendre le dimanche 12 avril minuit au plus tard

Au vu des événements actuels, je reporte la date limite de rendu (qui était le 29 mars 2020) au dimanche 12 avril minuit au plus tard


Comment le rendre

Faites un fichier README.txt et déposez-le ici
Dans le fichier README.txt, précisez :

  • l’adresse de votre site
  • un nom d’utilisateur
  • un mot de passe
  • (et plusieurs nom/mot de passe, s’il y a plusieurs niveaux de droits (administrateur/visiteur etc.))
  • si vous avez utilisé des librairies spécifiques que je vous ai autorisées, merci de le re-préciser

Sujet

Ce que vous voulez tant que c’est dans le cadre de ce que l’on a vu. Vous avez tout le Web comme inspiration !
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !
Si vous gérez des profils différents (admin / user ou autre), donnez moi les noms et mots de passe de différents profils !


Fonctionnalités obligatoires

  • Connexion + déconnexion (vu en cours)
  • Effets jQuery sur les éléments
  • Appels JSON : au moins deux appels en plus de ceux vus en cours

Sujets possibles

  1. Site de partage de photos
  2. Site de cocktails (cf ci-dessus)
  3. e-rated : site d’appréciations (selon des sujets, à définir)
  4. Ask-a-question : site où l’on pose des questions sur des sujets divers, et des gens répondent
  5. Write-a-book-together : site où l’on se connecte et où on peut écrire un livre à plusieurs
  6. Wedding-couple-site : site où l’on uploade + partage des photos de mariage + livre de commandes
  7. Playing-cards-collection : site où on scanne + échange des cartes (Magic the gathering)
  8. Polls-and-surveys : site de création de sondages (= QCM, exemple très beau ici : quipoquiz)
  9. Poems-generator : faire un cadavre exquis qui génère des poèmes + possibilité pour les utilisateurs de les noter / d’ajouter des mots
  10. The-future-of-post-it : faire un carnet de choses à faire pour les utilisateurs, qui envoie des mails de rappels de ces choses à des dates données
  11. Gift-ideas : un site où l’on va faire des idées de cadeaux / suggérer des idées de cadeaux + les noter (les meilleurs ressortent en premier)
  12. Le-bon-recoin : refaire le bon coin en plus simple
  13. Suggest-crawlers : site de suggestions : on clique sur un mot, il en suggère plein d’autres avec + définitions / liens de sites pour chacuns
  14. Tv-fans : site de présentations + notes d’émissions télé
  15. Faire le jeu SokoBan vu en cours, avec la possibilité de login, enregistrement. Pour les appels JSON supplémentaires, lorsque l’utilisateur choisit un tableau, s’en souvenir (= AJAX) et lorsqu’il se reconnecte, le remettre directement. Puis enregistrer son score lorsqu’il a terminé un niveau + montrer les meilleurs scores.

Pour les sujets qui suivent, ils sont possibles mais plutôt complexes et demandent plus d’investissement. Si vous êtes motivés, demandez-moi plus d’informations, je vous expliquerai les difficultés que vous allez rencontrer.

  1. Turn-by-turn : faire un jeu multijoueurs en tour par tour (jeu de cartes, de poker, ou de plateau etc)
  2. Chat-with-someone : site de chat/discussion
  3. A-maze-ing : site où l’on peut se ballader dans un labyrinthe et essayer d’en trouver la sortie

Sujet imposé si vous n’avez pas d’idée

Cocktails : on se connecte, on a une liste d’éléments (récupérés en JSON) disponibles, on coche ceux qui nous intéressent, on valide, c’est envoyé, et le retour en JSON affiche les cocktails qu’il est possible de faire avec ce que l’on a coché.


Ce que vous devez rendre

Idéalement

Une Url vers un site Web (utilisez Alwaysdata par exemple)

Si vous n’avez pas le choix

Les fichiers source de votre projet


Pour favoriser votre organisation

Utilisez ce que l’on a vu en cours (Google boilerplate)


Librairies autorisées

Interdiction d’utiliser une librairie JavaScript qui ne vienne pas des sites autorisés précédemment


Retard

Après le dimanche 12 avril minuit

Passé ce délai ce sera 1 pt par 2 heures de retard (je prendrai en compte la date de réception du mail).
Pour ceux qui essaient vraiment d’aller jusqu’à la dernière minute, toute heure entamée est comptée comme une heure complète. Exemple : un point en moins si je le reçois le 3 avril à 00:01.
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !


Copier-coller

  • Copie sur une autre personne (« je se savais pas comment implémenter telle ou telle fonctionnalité dont j’avais besoin pour aller plus loin, je l’ai copiée sur un autre ») :
    • si la personne est clairement nommée : note pour la fonctionnalité divisée par 2 (uniquement la moitié du travail a été faite) ;
    • 0 aux deux personnes sinon ;
  • Si je m’aperçois que vous avez bêtement copié collé des sources Internet, je vous convoquerai pour vous demander de m’expliquer la fonctionnalité, et :
    • si vous ne savez pas m’expliquer le code alors 0 ;
    • si vous savez m’expliquer tout le code alors votre note totale sera divisée par vous + le nombre de contributeurs à ce projet, ce qui se rapprochera certainement de 0 aussi.

Voici un exemple de ce que vous pouvez faire, si vous choisissez le projet cocktails.


PDFs

JavaScript
jQuery

Django : le polymorphisme : astuces

Un excellent article écrit par Dan Bader (j’ai pas mal discuté avec lui et j’ai son livre, il est vraiment aussi bon que modeste, je vous recommande vivement tous ses articles – et n’hésitez pas à lui parler en Anglais (tant qu’il n’est pas encore trop connu !), il vous répondra sûrement).

Il manque juste ces « petits » détails techniques lorsqu’on se base sur les modèles abstraits et l’héritage au sens « modèle » de Django (Abstract Base Model).

Prenons un exemple simple : vous créez un modèle « simple » Media(models.Model) dont vous allez hériter sur deux modèles : Book(Media) et Dvd(Media).

Ce qui va vous prendre beaucoup de temps (et qui n’est expliqué nulle part de manière claire et directe), c’est que Django, de manière cachée, va créer des propriétés qui vont dans les deux sens entre le modèle parent et le modèle enfant.

Pour reprendre notre exemple, avec les trois modèles : Media = parent, et les deux enfants Book et Dvd.
Imaginez que pour chaque enfant, vous créiez une méthode spécifique, par exemple Book.nb_pages() et Dvd.is_blue_ray()
De manière cachée, vous pourrez à partir du parent, accéder à l’enfant et ses propriété via son nom comme ceci :
my_media = Media.object.get(pk=1)
if hasattr(self, 'book'):  # this Media is a Book:
    my_media.book.nb_pages()
elif hasattr(self, 'dvd'):  # this media is a Dvd:
    my_media.dvd.is_blue_ray()
else:  # direct Media model access:
    pass

A l’inverse, à partir des modèles enfants, pour accéder au parent c’est le nom du modèle parent avec _ptr :
my_book = Book.object.get(pk=21)
my_dvd = Dvd.object.get(pk=75)
# imaginons que "price" est une propriété de Media property. On peut écrire :
print(my_book.media_ptr.price)
print(my_dvd.media_ptr.price)

Django : favicon.ico : comment le gérer facilement

En mode débug, un serveur Django renvoie tous les fichiers, y compris les fichiers statique. Seul problème : tous les fichiers statique on leur URL qui commence par /static/.

Mais on rencontre un problème : le favicon.ico n’est pas un fichier statique « normal » = qui commence par /static/. C’est un nom « en dur », qui ressemble à http://monsite/favicon.ico.

Il vous faut donc la coder en dur dans les URLs : ainsi : url(r'^favicon.ico/$',...)
Et pour renvoyer directement le contenu, il suffit de faire une fonction immédiate (= lambda) qui renvoie directement le contenu :
urlpatterns = [
    # ...blabla... all path and now:
    url(r'^public/(?P.*)$', static.serve, {
        'document_root': settings.MEDIA_ROOT
    }, name='url_public'),
    url(r'^favicon.ico/$', # google chrome favicon fix :
        lambda x: HttpResponseRedirect(settings.STATIC_URL+'favicon.ico')),

]

Django : comment passer une constante à tous vos templates ?

Django, nativement, a la possibilité de passer des dictionnaires clé-valeurs à tous les templates, et dans les templates on accède aux valeurs via la clé.

Exemple concret : je voulais passer le nom de mon site à tous les templates, mais sous forme de constante.

Dans settings.py j’ai défini ma constante : WEBSITE_NAME = 'mywebsite'

Il suffit de créer une fonction qui renvoie un dictionnaire avec une clé nommée « correctement », par exemple :

def context_processor_website_name(request):
    return {'website_name': WEBSITE_NAME}

Et ensuite, dans les context_processors, juste ajouter le nom de la fonction par rapport au package où elle est.
J’ai mis cette fonction dans settings.py.
Je ne sais pas si c’est la meilleure place mais l’idée c’est que comme c’est en rapport avec la configuration de mon site, c’est le meilleur endroit…

Donc pour préciser où est la fonction c’est 'myproject.settings.context_processor_website_name' :

On retrouve donc le code final comme ceci :

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates')],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            # ... and:
            'myproject.settings.context_processor_website_name'
    ],
    },
}, ]

Et voilà, à partir de maintenant, avec exactement 3 lignes de code, j’ai accès à ma variable website_name, que je peux utiliser ainsi :

<title>{% block title %}{% if title %}{{ title|safe }} - {% endif %}{{ website_name }}{% endblock %}</title>

Petite parenthèse : grâce à ce code, si la page qu’on affiche a un titre, elle aura pour titre : [titre] - [nom du site] et si elle n’a aucun titre, elle affichera : [nom du site]

Django : un modèle sur mesure : DaysField code source complet

Code source complet de DaysOfWeek

Après avoir écrit en trois parties (1, 2 et 3) comment faire un modèle sur mesure, voici le code source au complet.

from django.core.exceptions import ValidationError
from django import forms
from django.db import models
from django.utils.translation import ugettext_lazy as _
class DaysOfWeek:
    DAYS = {1: _("Monday"),
            2: _("Tuesday"),
            3: _("Wednesday"),
            4: _("Thursday"),
            5: _("Friday"),
            6: _("Saturday"),
            7: _("Sunday"), }
    DAYS_SHORT = {1: _("Mo"),
                  2: _("Tu"),
                  3: _("We"),
                  4: _("Th"),
                  5: _("Fr"),
                  6: _("Sa"),
                  7: _("Su"), }
    CHOICES = [(idx, value) for idx, value in DAYS.items()]
    @staticmethod
    def summary_from_list(tab, empty=' '):
        if tab is None:
            return '-'.join([empty for a in range(len(DaysOfWeek.DAYS))])
        return '-'.join([str(DaysOfWeek.DAYS_SHORT.get(i, empty)) for i in tab])
class DaysFormField(forms.TypedMultipleChoiceField):
    # different widget, comment to change interface:
    widget = forms.CheckboxSelectMultiple
    def __init__(self, *args, **kwargs):
        if 'max_length' in kwargs:
            kwargs.pop('max_length')
        kwargs['choices'] = DaysOfWeek.CHOICES
        super().__init__(*args, **kwargs)
class DaysField(models.CharField):
    description = _("Comma-separated integers between 1 and 7")
    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 13 # max. len = all days = "1,2,3,4,5,6,7" = 13
        super().__init__(*args, **kwargs)
    @staticmethod
    def value_to_array(value):
        if value is None:
            return None
        try:
            if isinstance(value, list):
                return [int(a) for a in value]
            elif isinstance(value, str):
                return [int(a) for a in value.split(',')]
        except (TypeError, ValueError):
            raise ValidationError(_("Unexpected value"))
        raise ValidationError(_("Unexpected value"))
    @staticmethod
    def from_db_value(value, expression, connection):
        return DaysField.value_to_array(value)
    def to_python(self, value):
        return DaysField.value_to_array(value)
    def get_prep_value(self, value):
        return ','.join([str(a) for a in value]) if value is not None else None
    def formfield(self, **kwargs):
        # ignore the admin directives: directly override with our custom form:
        return super().formfield(form_class=DaysFormField,
                                 initial=[])
    # region - Validator -
    class ListBetween1And7Validator:
        def __call__(self, value):
            try:
                if isinstance(value, list):
                    loop = [int(a) for a in value]
                elif isinstance(value, str):
                    loop = [int(a) for a in value.strip('[]').split(',')]
                else:
                    raise ValueError()
                for v in loop:
                    if not (7 >= v >= 1):
                        raise ValueError()
            except (TypeError, ValueError):
                raise ValidationError(
                    _("Enter a list if coma-separated values between 1 and 7."),
                    code='invalid')
    # endregion - Validator -
    default_validators = [ListBetween1And7Validator()]

Bootstrap : classes utilitaires

Vous allez peut-être trouver du code qui ressemble à cela :
<p class="mb-0">Texte dans un paragraphe</p>

Bootstrap >= 4 a mis en place plein de possibilités pour faire du padding et des marges « responsives ».
Elles fonctionnent pour tous les breakpoints :

  • xs (<=576px)
  • sm (>=576px)
  • md (>=768px)
  • lg (>=992px)
  • xl (>=1200px)

<p class="{propriété}{côtés}-{taille}">texte</p>

propriété :

  • m – les marges
  • p – le padding

côtés :

  • tmargin-top or padding-top
  • bmargin-bottom or padding-bottom
  • lmargin-left or padding-left
  • rmargin-right or padding-right
  • x*-left and *-right
  • y*-top and *-bottom
  • blank – les 4 côtés à la fois

taille :

  • 0 – supprimer les marges ou mettre le padding à 0
  • 1 – $spacer *0.25
  • 2 – $spacer *0.5
  • 3 – $spacer
  • 4 – $spacer *1.5
  • 5 – $spacer *3
  • auto – margin à auto