2012-03-01 9 views
32

ho modello UserProfile con il campo avatar = models.ImageField(upload_to=upload_avatar)ImageField file immagine sovrascrittura con lo stesso nome

upload_avatar nomi di funzioni file di immagine in base user.id (12.png per esempio).

Ma quando l'utente aggiorna l'avatar, il nuovo nome dell'avatar coincide con il vecchio nome dell'avatar e Django aggiunge il suffisso al nome del file (12-1.png ad esempio).

C'è un modo per sovrascrivere il file anziché creare il nuovo file?

+0

È possibile aggiungere data e ora per la vostra immagine attuale .. Si potrebbe utilizzare come suffisso .. come '2016_987890_image.jpg' .. questo ti aiuterà anche nell'immagine di serching – aryan

risposta

51

Sì, questo è venuto anche per me. Ecco cosa ho fatto.

Modello:

from app.storage import OverwriteStorage 

class Thing(models.Model): 
    image = models.ImageField(max_length=SOME_CONST, storage=OverwriteStorage(), upload_to=image_path) 

definito anche in models.py:

def image_path(instance, filename): 
    return os.path.join('some_dir', str(instance.some_identifier), 'filename.ext') 

in un file separato, storage.py: i valori dei campioni

from django.core.files.storage import FileSystemStorage 
from django.conf import settings 
import os 

class OverwriteStorage(FileSystemStorage): 

    def get_available_name(self, name): 
     """Returns a filename that's free on the target storage system, and 
     available for new content to be written to. 

     Found at http://djangosnippets.org/snippets/976/ 

     This file storage solves overwrite on upload problem. Another 
     proposed solution was to override the save method on the model 
     like so (from https://code.djangoproject.com/ticket/11663): 

     def save(self, *args, **kwargs): 
      try: 
       this = MyModelName.objects.get(id=self.id) 
       if this.MyImageFieldName != self.MyImageFieldName: 
        this.MyImageFieldName.delete() 
      except: pass 
      super(MyModelName, self).save(*args, **kwargs) 
     """ 
     # If the filename already exists, remove it as if it was a true file system 
     if self.exists(name): 
      os.remove(os.path.join(settings.MEDIA_ROOT, name)) 
     return name 

Ovviamente, questi sono qui , ma nel complesso questo funziona bene per me e questo dovrebbe essere abbastanza semplice da modificare se necessario.

+0

Grazie, ci sono quello che mi serve, forse sai come cancellare il file immagine, quando l'utente cancella il suo avatar? – Deadly

+0

Non sono sicuro di cosa intendi per "cancella il loro avatar", ma l'os.remove (nome file) dovrebbe eliminare tutto ciò che è necessario eliminare stai chiedendo qualcos'altro? – Lockjaw

+0

Intendo la casella di controllo "cancella l'avatar", generata da 'forms.ImageField' nei moduli. – Deadly

4

Si può provare a definire il proprio Filesystemstorage e sovrascrivere il metodo predefinito get_availbale_name.

from django.core.files.storage import FileSystemStorage 
import os 

class MyFileSystemStorage(FileSystemStorage): 
    def get_available_name(self, name): 
     if os.path.exists(self.path(name)): 
      os.remove(self.path(name)) 
     return name 

Per la vostra immagine che si potrebbe definire un fs in questo modo:

fs = MyFileSystemStorage(base_url='/your/url/', 
    location='/var/www/vhosts/domain/file/path/') 
avatar = models.ImageField(upload_to=upload_avatar, storage=fs) 

Spero che questo aiuti.

+0

Questo codice pende Django quando salvo il profilo (nessuna risposta dal server) :( – Deadly

+0

In effetti si blocca, dovrai prima eliminare il file esistente Vedi la risposta aggiornata – Jingo

+0

Grazie, ora funziona bene, – Deadly

8

ahem ... potrebbe sembrare non ortodosso, ma la mia soluzione, al momento, è di controllare & rimuovere il file esistente all'interno del callback che già utilizzo per fornire il nome del file caricato. In models.py:

import os 
from django.conf import settings 

def avatar_file_name(instance, filename): 
    imgname = 'whatever.xyz' 
    fullname = os.path.join(settings.MEDIA_ROOT, imgname) 
    if os.path.exists(fullname): 
     os.remove(fullname) 
    return imgname 
class UserProfile(models.Model): 
    avatar = models.ImageField(upload_to=avatar_file_name, 
           default=IMGNOPIC, verbose_name='avatar') 
+0

Questo non sembra funzionare per me :( – GobSmack

8

è possibile scrivere classe di archiviazione ancora meglio in questo modo:

class OverwriteStorage(FileSystemStorage): 

    def get_available_name(self, name): 
     self.delete(name) 
     return name 
+6

'self.delete' controllerà se il file esiste, quindi il controllo 'self.exists' non è necessario. – vikki

12
class OverwriteStorage(get_storage_class()): 

    def _save(self, name, content): 
     self.delete(name) 
     return super(OverwriteStorage, self)._save(name, content) 

    def get_available_name(self, name): 
     return name 
+0

Perché non risparmi questa risposta? , è meglio sovrascrivere _save per la cancellazione del file piuttosto che aspettarsi get_available_name per chiamare semplicemente una volta prima del metodo di salvataggio ... Può portare a bug se lo usi da qualche altra parte nel codice per qualche motivo ... – Kukosk

+0

@Kukosk Questo risposta non ha più upvotes soprattutto perché è di 3 anni in ritardo e forse perché manca una descrizione –

+2

FWIW Preferisco non sovrascrivere i metodi interni di una classe genitore. Nell'evento _save() viene rimosso o ha il suo argomento cambiato questo codice si interromperà e il cambiamento non sarà menzionato nelle note di rilascio poiché non fa parte dell'API pubblica – saschwarz

3

Basta refference il vostro modello di campo immagine, eliminarlo e salvare di nuovo.

model.image.delete() 
model.image.save() 
+0

preferisco in questo modo. grazie – nuttynibbles

+0

Risposta più semplice. Grazie. –

0

Ho provato le soluzioni di cui qui. Ma sembra non funzionare a Django 1.10. Sarebbe aumentare il seguente errore da qualche parte al modello del admin:

url() missing 1 required positional argument: 'name'

Così mi si avvicinò con la mia soluzione, che consiste sulla creazione di un segnale pre_save che cerca di ottenere l'istanza dal database prima che sia salvato e elimina del percorso del file:

from django.db.models.signals import pre_save 


@receiver(pre_save, sender=Attachment) 
def attachment_file_update(sender, **kwargs): 
    attachment = kwargs['instance'] 
    # As it was not yet saved, we get the instance from DB with 
    # the old file name to delete it. Which won't happen if it's a new instance 
    if attachment.id: 
     attachment = Attachment.objects.get(pk=attachment.id) 
     storage, path = attachment.its_file.storage, attachment.its_file.path 
     storage.delete(path) 
3

per Django 1.10 Ho trovato ho dovuto modificare la risposta superiore per includere l'argomento max_length nella funzione:

from django.core.files.storage import FileSystemStorage 
import os 

class OverwriteStorage(FileSystemStorage): 
def get_available_name(self, name, max_length=None): 
    if self.exists(name): 
     os.remove(os.path.join(settings.MEDIA_ROOT, name)) 
    return name 
Problemi correlati