Django: acceso a la clave principal en la ubicación de models.filefield (upload_to)

Me gustaría guardar mis archivos usando la clave principal de la entrada.

Aquí está mi código:

def get_nzb_filename(instance, filename): if not instance.pk: instance.save() # Does not work. name_slug = re.sub('[^a-zA-Z0-9]', '-', instance.name).strip('-').lower() name_slug = re.sub('[-]+', '-', name_slug) return u'files/%s_%s.nzb' % (instance.pk, name_slug) class File(models.Model): nzb = models.FileField(upload_to=get_nzb_filename) name = models.CharField(max_length=256) 

Sé que la primera vez que se guarda un objeto, la clave principal no está disponible, por lo que estoy dispuesto a dar un golpe adicional para guardar el objeto solo para obtener la clave principal y luego continuar.

El código anterior no funciona. Lanza el siguiente error:

 maximum recursion depth exceeded while calling a Python object 

Supongo que esto es un bucle infinito. Llamar al método de save llamaría al método get_nzb_filename , que nuevamente llamaría al método de save , y así sucesivamente.

Estoy usando la última versión del tronco Django.

¿Cómo puedo obtener la clave principal para poder usarla para guardar los archivos cargados?


Actualizar @muhuk:

Me gusta tu solución. ¿Puedes ayudarme a implementarlo? He actualizado mi código a lo siguiente y el error es 'File' object has no attribute 'create' . Tal vez estoy usando lo que has escrito fuera de contexto?

 def create_with_pk(self): instance = self.create() instance.save() return instance def get_nzb_filename(instance, filename): if not instance.pk: create_with_pk(instance) name_slug = re.sub('[^a-zA-Z0-9]', '-', instance.name).strip('-').lower() name_slug = re.sub('[-]+', '-', name_slug) return u'files/%s_%s.nzb' % (instance.pk, name_slug) class File(models.Model): nzb = models.FileField(upload_to=get_nzb_filename, blank=True, null=True) name = models.CharField(max_length=256) 

En lugar de aplicar el campo requerido en mi modelo, lo haré en mi clase de Formulario. No hay problema.

Parece que primero deberá generar sus modelos de File con campos de archivo vacíos. Luego toma uno y guárdalo con el objeto de archivo dado.

Puedes tener un método de administrador personalizado como este;

 def create_with_pk(self): instance = self.create() instance.save() # probably this line is unneeded return instance 

Pero esto será problemático si cualquiera de sus campos es requerido. Debido a que inicialmente está creando un objeto nulo, no puede imponer los campos obligatorios en el nivel del modelo.

EDITAR

create_with_pk se supone que es un método de administrador personalizado , en su código es solo un método regular. Por lo tanto, el self tiene sentido. Todo está debidamente documentado con ejemplos.

Puede hacerlo configurando upload_to en una ubicación temporal y creando un método de guardado personalizado.

El método de guardar debe llamar súper primero, para generar la clave principal (esto guardará el archivo en la ubicación temporal). Luego, puede cambiar el nombre del archivo con la clave principal y moverlo a su ubicación correcta. ¡Llama al super una vez más para guardar los cambios y listo! Esto me funcionó bien cuando me topé con este problema exacto.

Por ejemplo:

 class File( models.Model ): nzb = models.FileField( upload_to='temp' ) def save( self, *args, **kwargs ): # Call save first, to create a primary key super( File, self ).save( *args, **kwargs ) nzb = self.nzb if nzb: # Create new filename, using primary key and file extension oldfile = self.nzb.name dot = oldfile.rfind( '.' ) newfile = str( self.pk ) + oldfile[dot:] # Create new file and remove old one if newfile != oldfile: self.nzb.storage.delete( newfile ) self.nzb.storage.save( newfile, nzb ) self.nzb.name = newfile self.nzb.close() self.nzb.storage.delete( oldfile ) # Save again to keep changes super( File, self ).save( *args, **kwargs ) 

Contexto

Tenía el mismo problema. Se resolvió al atribuir una identificación al objeto actual guardando primero el objeto.

Método

  1. crear una función personalizada upload_to
  2. detectar si el objeto tiene pk
  3. si no, guarda la instancia primero, recupera el pk y asignalo al objeto
  4. genera tu camino con eso

Código de trabajo de muestra:

 class Image(models.Model): def upload_path(self, filename): if not self.pk: i = Image.objects.create() self.id = self.pk = i.id return "my/path/%s" % str(self.id) file = models.ImageField(upload_to=upload_path) 

Ty, ¿hay alguna razón por la que hayas lanzado tu propio filtro slugify?

Django se envía con un filtro slugify de slugify , puede usarlo así:

 from django.template.defaultfilters import slugify slug = slugify(some_string) 

No estoy seguro si sabías que estaba disponible para usar …