2

I have these models:

class Project():
    name = models.CharField(max_length=512)

class Task():
    name = models.CharField(max_length=256)
    project = models.ForeignKey('prosystem.Project',
                                related_name='tasks',
                                on_delete=models.CASCADE)

class TaskFile(models.Model):
    task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='tasks')
    file = models.FileField(upload_to=self.make_file_path())        # I want this to be dynamic path

    def make_file_path(self):
       # pseudocode, does not work
       pid = self.task.project.id
       tid = self.task.id
       path = f'project_{pid}/task_{tid}/'
       return path

I want to upload files to a folder based on its Task id and its parent Project id. How can I do that?

Chiefir
  • 2,561
  • 1
  • 27
  • 46
  • This looks to me like a very bad idea: imagine that the name of the `Task` or `Project` changes its name, then this will require all sorts of extra logic (and there are ways in the Django ORM to bypass this logic) to *rename* the file. In my opinion, the filename on the *server* is not that relevant. You better ensure that on the *frontend* the filename is *presented* with a "nice" name. – Willem Van Onsem Oct 02 '18 at 08:57
  • @Willem Van Onsem but I am talking about a *path* to a file, not its *name*. Are there any differences to what you have written in that case? – Chiefir Oct 02 '18 at 08:58
  • no, since a path is in essence nothing more than a *sequence* of names (so the above stated problem holds). – Willem Van Onsem Oct 02 '18 at 08:59
  • @Willem Van Onsem ok thank you about that notatation, I will think about that. But if I agree with that - any advices how to build that path as I want? – Chiefir Oct 02 '18 at 09:01
  • @Willem Van Onsem btw, changed naming from using simple *name* to models' *id* fields. – Chiefir Oct 02 '18 at 09:26

2 Answers2

1

This should do what you need:

class Project():
    name = models.CharField(max_length=512)

class Task():
    name = models.CharField(max_length=256)
    project = models.ForeignKey('prosystem.Project',
                                related_name='tasks',
                                on_delete=models.CASCADE)

def make_file_path(instance, filename):

   pid = instance.task.project.id
   tid = instance.task.id
   path = f'project_{pid}/task_{tid}/{filename}'

   return path

class TaskFile(models.Model):
    task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='tasks')

    # Please note that "()" have been removed here. You don't want to
    # give the result of make_file_path() but the function itself
    file = models.FileField(upload_to=make_file_path)

For more information, refer to the documentation about FileField.upload_to.

Antwane
  • 20,760
  • 7
  • 51
  • 84
  • will `def make_file_path` be a part of `TaskFile` model? – Chiefir Oct 02 '18 at 09:24
  • No, it's a global function, defined in the module. You can put it anywhere you want as long as you correctly import it – Antwane Oct 02 '18 at 09:26
  • You may declare `make_file_path` inside your model, but in such case you will need to declare it as `@classmethod` or `@staticmethod` – Antwane Oct 02 '18 at 09:29
  • if I use it with `classmethod` - should i add `cls` as first argument? – Chiefir Oct 02 '18 at 09:31
  • and how can I use this method inside field? prepended with `self.`? Pycharm shows me bad syntax now. – Chiefir Oct 02 '18 at 09:32
  • Yes, with class method you have to use `cls` as first arg. See [this post](https://stackoverflow.com/a/1669524/1887976) for more and complete information. Use the method with `TaskFile.make_file_path` from your model field declaration – Antwane Oct 02 '18 at 09:33
  • this works with global function, thank you. If you can - just update your answer with id_strings instead of names, as I have updated my question - for convenience of future readers. – Chiefir Oct 02 '18 at 11:14
  • 1
    I updated my answer with the new path syntax you provided – Antwane Oct 02 '18 at 11:23
0

You need to create a CustomFileStorage and add it to your file field. Info here

Bestasttung
  • 2,388
  • 4
  • 22
  • 34