Django : invoquer le super en premier ou non
Bonjour,
J'ai tendance à suivre la documentation et à retourner le super quand je surcharge form_valid.
Mais j'ai un pro qui m'a envoyé le message ci-dessous.
Du coup il vaut mieux invoquer le super dans une variable ?
j'ai du mal à comprendre ce que ça change.
Merci d'avance
class CreateAddress(LoginRequiredMixin, CreateView):
model = ExChangerAdresses
template_name = "accounts/create-address.html"
fields = ["name", "address_1", "address_2", "city", "zip_code", "country"]
success_url = reverse_lazy("shop:address-choice")
def form_valid(self, form):
form.instance.user = self.request.user
self.request.user.adresses.filter(default=True).update(default=False)
form.instance.default = True
return super().form_valid(form)
"Invoque le super en premier en l’assignant à une variable, ensuite tu peux faire ce dont tu as besoin et retournes la variable
Invoquer le super en premier te permet d’être certain que l’instance est commit immédiatement en db
"
Salut Gab :)
Pour bien comprendre il faut remonter aux concepts de l'orienté objet (Django ici ne fait rien de plus en particulier).
Quand tu appelles super().form_valid(form) au début, tu appelle en premier la méthode form_valid de la classe dont tu hérites. Donc tu vas en premier lieu faire toutes les opérations opérées par Django normalement quand tu appelles form_valid. Et par la suite, tu fais tes opérations à toi.
Si tu fais l'inverse, ça veut dire que tu fais déjà tes opérations, puis celles de Django.
En le faisant de cette manière, tu t'assures que Django a bien réalisé sa partie avant que tu interviennes.
Dans certains cas ça peut totalement changer voir casser ce que tu fais. Par exemple dans les méthodes save, si tu mets le super au début, tu vas déjà sauvegarder le modèle dans la BDD et ensuite quand tu fais tes opérations le modèle sera du coup déjà dans la base.
Alors que si tu fais l'inverse, tu opères avant que ton instance soit dans la base de données. Donc ça peut totalement changer le résultat.
Je te montre un exemple simplifié pour comprendre ce principe avec de l'OO simple.
Imagine une classe Parent et une classe Child qui hérite de Parent. Chacune a une méthode do_something() que Child surcharge.
class Parent:
def do_something(self):
print('Parent fait quelque chose')
return 'Parent est fini!'
class Child1(Parent):
def do_something(self):
print('Child1 fait quelque chose')
result = super().do_something()
print(result)
return 'Child1 est fini aussi!'
class Child2(Parent):
def do_something(self):
result = super().do_something()
print('Child2 fait quelque chose')
print(result)
return 'Child2 est fini aussi!'
Si tu fais Child1().do_something(), tu obtiens:
Child1 fait quelque chose
Parent fait quelque chose
Parent est fini!
Child1 est fini aussi!
Mais si tu fais Child2().do_something(), c'est différent:
Parent fait quelque chose
Child2 fait quelque chose
Parent est fini!
Child2 est fini aussi!
Dans le cas de Child1, Child1 a fait quelque chose en premier, puis Parent. Mais dans Child2, Parent a fait quelque chose d'abord, puis Child2.
C'est pour ça que quand tu invoques super() en premier tu demandes à Django qu'il fasse d'abord ce qu'il a à faire avant d'ajouter ton propre code.
Oui merci ! :)
C'est pour ça que save() je l'invoque toujours en dernier.
Mais pour form_valid, quel serait l'intérêt pour toi d'invoquer le super en premier ?
Car personnellement avec form_valid je fais ce que j'ai à faire avant et j'appelle super à la fin.
ça fait un peu echo à ma question précédente où je n'utilise pas commit=False, j'opère mes actions avant le save en BDD.
En fait j'ai du mal à voir ce qui est vraiment le mieux. Après je me pose peut être trop de questions pas forcément utiles en soi.
C'est surtout pourquoi créer en BDD l'instance en appelant d'abord le super ? Car on peut très bien travailler sur notre instance et la sauvegarder à la fin.
EDIT : apparemment ça pourrait poser un potentiel problème de "race condition" si on ne commit pas immédiatement en BDD
Salut Gab !
Alors pour form_valid, c'est le même principe que j'expliquais avant avec l'exemple. En invoquant le super en premier, tu te assures que tout ce qui doit être fait (et qui a été prévu par Django) soit bien fait avant que tu n'interviennes.
Dans le cas du form_valid (ou même n'importe quelle méthode), il faut voir ce que fait la méthode et après évaluer si tu veux / pense que c'est primordial de le faire avant ou après.
Pour le coup de la "race condition" effectivement je peux imaginer dans certains cas un problème.
Une "race condition" c'est quand tu as deux personnes qui font quelque chose sur un élément qui est ouvert, ça peut être le cas sur des fichiers, ou des bases de données. C'est pour ça par exemple que sur des fichiers, si quelqu'un ouvre le fichier et fait des modifications, généralement tu as un "lock" qui permet d'éviter que quelqu'un puisse ouvrir le fichier en même temps. Le risque étant que quelqu'un commence des modifications, quelqu'un d'autre également, et le dernier qui sauvegarde efface les changements du premier.
Dans ton cas imagine que deux utilisateurs soient en train de créer une adresse en même temps. Si tu ne fais pas le commit tout de suite avec super().form_valid, tu pourrais te retrouver dans une situation où tu as modifié quelque chose (par exemple, changer tous les autres défauts à False) mais ensuite un autre utilisateur fait la même manipulation.
Donc lorsqu'il te faut sauvegarder réellement (lors de l'appel de super), la base de données a déjà changé entre temps. Donc tes actions ont été basées sur de "vieilles" données.
Inscris-toi
(c'est gratuit !)
Tu dois créer un compte pour participer aux discussions.
Créer un compte