{"title":"Écriture d’un système de stockage personnalisé","version":"3.0","locale":"fr","docname":"howto/custom-file-storage","url":"/fr/3.0/howto/custom-file-storage/","canonical":"https://djangodocs.dev/fr/3.0/howto/custom-file-storage/","summary":"Si vous avez besoin de fournir un stockage de fichiers personnalisé, typiquement pour stocker des fichiers dans un système distant, vous pouvez le faire en…","html":"<h1>Écriture d’un système de stockage personnalisé<a class=\"heading-anchor\" href=\"#writing-a-custom-storage-system\"><span class=\"visually-hidden\">Lien vers cette rubrique</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>Si vous avez besoin de fournir un stockage de fichiers personnalisé, typiquement pour stocker des fichiers dans un système distant, vous pouvez le faire en définissant une classe de stockage personnalisée. Il faudra suivre ces étapes :</p>\n<ol class=\"arabic\">\n<li><p>Votre système de stockage personnalisé doit être une sous-classe de <code class=\"docutils literal notranslate\"><span class=\"pre\">django.core.files.storage.Storage</span></code>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.files.storage</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Storage</span>\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyStorage</span><span class=\"p\">(</span><span class=\"n\">Storage</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span>\n</code></pre></div>\n</li>\n<li><p>Django doit être capable d’instancier votre système de stockage sans paramètre. Cela signifie que tout réglage doit provenir de <code class=\"docutils literal notranslate\"><span class=\"pre\">django.conf.settings</span></code>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.conf</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">settings</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.files.storage</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Storage</span>\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyStorage</span><span class=\"p\">(</span><span class=\"n\">Storage</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">option</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">option</span><span class=\"p\">:</span>\n            <span class=\"n\">option</span> <span class=\"o\">=</span> <span class=\"n\">settings</span><span class=\"o\">.</span><span class=\"n\">CUSTOM_STORAGE_OPTIONS</span>\n        <span class=\"o\">...</span>\n</code></pre></div>\n</li>\n<li><p>Votre classe de stockage doit implémenter les méthodes <a class=\"reference internal\" href=\"#django.core.files.storage._open\" title=\"django.core.files.storage._open\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">_open()</span></code></a> et <a class=\"reference internal\" href=\"#django.core.files.storage._save\" title=\"django.core.files.storage._save\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">_save()</span></code></a>, en plus de toute autre méthode adéquate pour votre classe de stockage. Voir ci-dessous pour plus de détails sur ces méthodes.</p>\n<p>De plus, si votre classe propose du stockage de fichiers locaux, elle doit surcharger la méthode <code class=\"docutils literal notranslate\"><span class=\"pre\">path()</span></code>.</p>\n</li>\n<li><p>Votre classe de stockage doit être <a class=\"reference internal\" href=\"/fr/3.0/topics/migrations/#custom-deconstruct-method\"><span class=\"std std-ref\">déconstructible</span></a> pour pouvoir être sérialisée lorsqu’elle est référencée dans un champ dans une migration. Tant que les paramètres du champ sont eux-mêmes <a class=\"reference internal\" href=\"/fr/3.0/topics/migrations/#migration-serializing\"><span class=\"std std-ref\">sérialisables</span></a>, vous pouvez utiliser le décorateur de classe <code class=\"docutils literal notranslate\"><span class=\"pre\">django.utils.deconstruct.deconstructible</span></code> pour cela (c’est ce que Django utilise pour <code class=\"docutils literal notranslate\"><span class=\"pre\">FileSystemStorage</span></code>).</p></li>\n</ol>\n<p>Par défaut, les méthodes suivantes génèrent <code class=\"docutils literal notranslate\"><span class=\"pre\">NotImplementedError</span></code> et doivent typiquement être surchargées :</p>\n<ul class=\"simple\">\n<li><p><a class=\"reference internal\" href=\"/fr/3.0/ref/files/storage/#django.core.files.storage.Storage.delete\" title=\"django.core.files.storage.Storage.delete\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.delete()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/fr/3.0/ref/files/storage/#django.core.files.storage.Storage.exists\" title=\"django.core.files.storage.Storage.exists\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.exists()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/fr/3.0/ref/files/storage/#django.core.files.storage.Storage.listdir\" title=\"django.core.files.storage.Storage.listdir\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.listdir()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/fr/3.0/ref/files/storage/#django.core.files.storage.Storage.size\" title=\"django.core.files.storage.Storage.size\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.size()</span></code></a></p></li>\n<li><p><a class=\"reference internal\" href=\"/fr/3.0/ref/files/storage/#django.core.files.storage.Storage.url\" title=\"django.core.files.storage.Storage.url\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Storage.url()</span></code></a></p></li>\n</ul>\n<p>Notez cependant que ces méthodes ne sont pas toutes obligatoires et peuvent être délibérément omises. Il est même possible d’obtenir une classe de stockage fonctionnelle sans implémenter aucune de ces méthodes.</p>\n<p>À titre d’exemple, si l’énumération du contenu de certains moteurs de stockage se révèle coûteuse, il est tout à fait possible de décider de ne pas implémenter <code class=\"docutils literal notranslate\"><span class=\"pre\">Storage.listdir()</span></code>.</p>\n<p>Un autre exemple pourrait être un moteur qui ne gère que l’écriture dans des fichiers. Dans ce cas, il ne serait pas nécessaire d’implémenter une seule des méthodes ci-dessus.</p>\n<p>Au final, le choix des méthodes à implémenter vous revient. En laissant certaines méthodes non implémentées, l’interface proposée sera partielle (voire défectueuse).</p>\n<p>Il est généralement souhaitable d’implémenter les méthodes tout spécialement conçues pour les objets de stockage personnalisé. Ce sont :</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage._open\">\n<span class=\"sig-name descname\"><span class=\"pre\">_open</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">mode</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">'rb'</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage._open\"><span class=\"visually-hidden\">Lien vers cette définition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p><strong>Obligatoire</strong>.</p>\n<p>Appelée par <code class=\"docutils literal notranslate\"><span class=\"pre\">Storage.open()</span></code>, c’est le mécanisme utilisé par la classe de stockage pour ouvrir un fichier. Elle doit renvoyer un objet <code class=\"docutils literal notranslate\"><span class=\"pre\">File</span></code>, même si dans la plupart des cas, elle renverra plutôt une sous-classe qui implémente la logique particulière du moteur de stockage.</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage._save\">\n<span class=\"sig-name descname\"><span class=\"pre\">_save</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">content</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage._save\"><span class=\"visually-hidden\">Lien vers cette définition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>Appelée par <code class=\"docutils literal notranslate\"><span class=\"pre\">Storage.save()</span></code>. Le paramètre <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> aura déjà passé par <code class=\"docutils literal notranslate\"><span class=\"pre\">get_valid_name()</span></code> et <code class=\"docutils literal notranslate\"><span class=\"pre\">get_available_name()</span></code>, et <code class=\"docutils literal notranslate\"><span class=\"pre\">content</span></code> sera un objet de type <code class=\"docutils literal notranslate\"><span class=\"pre\">File</span></code>.</p>\n<p>Elle doit renvoyer le nom réel du fichier enregistré (normalement le paramètre <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code>, mais si le stockage a besoin de modifier le nom de fichier, c’est le nouveau nom qui devra être renvoyé).</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage.get_valid_name\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_valid_name</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage.get_valid_name\"><span class=\"visually-hidden\">Lien vers cette définition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>Renvoie un nom de fichier acceptable par le système de stockage sous-jacent. Le paramètre <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> passé à cette méthode est soit le nom de fichier original envoyé au serveur, soit, si <code class=\"docutils literal notranslate\"><span class=\"pre\">upload_to</span></code> est exécutable, le nom de fichier renvoyé par cette méthode dépourvu de toute notion de chemin. Surchargez cette méthode pour personnaliser la manière dont des caractères non standards sont convertis pour former des noms de fichiers sûrs.</p>\n<p>Le code fourni par <code class=\"docutils literal notranslate\"><span class=\"pre\">Storage</span></code> ne conserve que les caractères alphanumériques, les points et les soulignements du nom de fichier original, supprimant tout le reste.</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage.get_alternative_name\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_alternative_name</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">file_root</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">file_ext</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage.get_alternative_name\"><span class=\"visually-hidden\">Lien vers cette définition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<aside class=\"version-note version-added\" data-version=\"3.0\">\n<p class=\"version-note-title\">New in Django 3.0</p></aside>\n<p>Renvoie un nom de fichier différent basé sur les paramètres <code class=\"docutils literal notranslate\"><span class=\"pre\">file_root</span></code> et <code class=\"docutils literal notranslate\"><span class=\"pre\">file_ext</span></code>. Par défaut, un soulignement suivi d’une chaîne aléatoire de 7 caractères alphanumériques sont ajoutés à la fin du nom du fichier avant l’extension.</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.core.files.storage.get_available_name\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_available_name</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">name</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">max_length</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.core.files.storage.get_available_name\"><span class=\"visually-hidden\">Lien vers cette définition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>Renvoie un nom de fichier disponible (non existant) dans le mécanisme de stockage, prenant potentiellement en compte le nom de fichier fourni. Le paramètre <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> transmis à cette méthode aura déjà été nettoyé afin d’être un nom de fichier valable pour le système de stockage, en accord avec la méthode <code class=\"docutils literal notranslate\"><span class=\"pre\">get_valid_name()</span></code> décrite ci-dessus.</p>\n<p>La longueur du nom de fichier ne dépassera pas <code class=\"docutils literal notranslate\"><span class=\"pre\">max_length</span></code>, si cette option est indiquée. Si un nom de fichier unique n’est pas disponible, une exception <a class=\"reference internal\" href=\"/fr/3.0/ref/exceptions/#django.core.exceptions.SuspiciousOperation\" title=\"django.core.exceptions.SuspiciousOperation\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">SuspiciousFileOperation</span></code></a> est générée.</p>\n<p>Si un fichier avec <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> existe déjà, <code class=\"docutils literal notranslate\"><span class=\"pre\">get_alternative_name()</span></code> est appelée pour obtenir un nom différent.</p>","rootId":"writing-a-custom-storage-system","toc":[],"breadcrumbs":[{"docname":"howto/index","title":"Guides pratiques","url":"/fr/3.0/howto/"}],"prev":{"docname":"howto/custom-template-tags","title":"Balises et filtres de gabarit personnalisés","url":"/fr/3.0/howto/custom-template-tags/"},"next":{"docname":"howto/deployment/index","title":"Déploiement de Django","url":"/fr/3.0/howto/deployment/"},"formats":{"html":"/fr/3.0/howto/custom-file-storage/","markdown":"/fr/3.0/howto/custom-file-storage.md","json":"/fr/3.0/howto/custom-file-storage.json"},"source":"https://github.com/django/django/blob/stable/3.0.x/docs/howto/custom-file-storage.txt","official":"https://docs.djangoproject.com/fr/3.0/howto/custom-file-storage/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11","1.10","1.9"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}