0

I'm trying to call the property protocolo on a new imagefield's upload_to argument

What I'm trying to accomplish is to have the saved images use a custom filename.

class biopsia(models.Model):
    paciente = models.CharField(max_length=50)
    creado = models.DateTimeField(auto_now_add=True)
    foto = models.ImageField(upload_to=f'fotos_biopsias/%Y/{protocolo}', blank=True)

    def __str__(self):
        return str(self.protocolo)

    @property
    def protocolo(self):
        return 'BIO' + str(self.creado.year) + '-' + str(biopsia._base_manager.filter(
            creado__year=self.creado.year,
            creado__lt=self.creado
        ).count() + 1)

File "models.py", line 30, in biopsia
    foto = models.ImageField(upload_to=f'fotos_biopsias/%Y/{protocolo}', blank=True)
NameError: name 'protocolo' is not defined 

I've tried defining an outside method for upload_to but still I cannot use it inside my class

luisbm
  • 3
  • 2
  • It's missing `self.` - however, note that you're doing this on a class attribute, so `self` won't be defined there. You probably want to turn `foto` into a property as well? Why are those defined directly on the class anyway? – Grismar Nov 21 '22 at 22:23
  • `protocolo` is definitely not defined where you try to reference it. If you moved the definition above so it would be defined when you try to use it, almost certainly, it would not give you what you want. Fundamentally, you are trying to use *instance state* in the *class*, but **no instance exists yet** (indeed, not even the *class* exists yet). So you really just fundamentally have a design problem – juanpa.arrivillaga Nov 21 '22 at 22:25
  • 1
    @Grismar I think it's a django, thing. Unfortunately django relies on a lot of metaclass magic. I've never been a big fan of that for this very reason - you get minor cosmetic benefits but it makes everything else awkward – juanpa.arrivillaga Nov 21 '22 at 22:26

1 Answers1

1

You can follow the official documentation on how to use function as path for ImageField. Basically, you need to define a function in outer scope of the Model class. For your case, you can try the following code:

def protocolo(instance, filename):
    return f'fotos_biopsias/{timezone.now().year}/BIO' + str(instance.creado.year) + '-' + str(instance.__class__._base_manager.filter(
        creado__year=instance.creado.year,
        creado__lt=instance.creado
    ).count() + 1) + "/" + filename


class biopsia(models.Model):
    paciente = models.CharField(max_length=50)
    creado = models.DateTimeField(auto_now_add=True)
    foto = models.ImageField(upload_to=protocolo, blank=False, null=False)

    def __str__(self):
        foto_path = self.foto.path.split('/')[3:]
        return '/'.join(foto_path)

To be honest, it feels over calculating in DB level for simple storing the images. If it is only about how to show image in a particular url path, you can consider writing a view which acts as wrapper at that url path for downloading images. You can follow this example given here: Django: Serving Media Behind Custom URL.

ruddra
  • 50,746
  • 7
  • 78
  • 101