2

I have user submitted content that I am trying to write to a file and then save to a FileField.

so I have a model that looks like this:

class Revision(models.Model):
    def custom_revision_file_path(instance, filename):
        return '/'.join(['content/revisions', filename])
    path = models.FileField(upload_to=custom_revision_file_path)
    document = models.ForeignKey(Document)
    ...

and the view that creates the instance looks like this:

def handle_revisions(request): 
    document = Document.objects.get(id=request.GET['docid'])
    basename = os.path.basename(str(document.path))

    revision = Revision.objects.create(
        document = document,
    )
    revision.path.save(basename, ContentFile(request.GET['revision']))

This all works relatively fine but for two problems:

1) the ContentFile puts a space between each letter in my string so 'test' turns into 't e s t' ;

2) for some reason each time I run the view two Revision instances are saved with roughly the same path. ie. one path will be 'content/revisions/test.txt' and the other will be 'content/revisions/test_1.txt' when the second one shouldn't exist at all.

What gives?

Stan
  • 8,710
  • 2
  • 29
  • 31
Daniel Nill
  • 5,539
  • 10
  • 45
  • 63
  • For anyone coming to this question. I've mostly moved on to a more elegant solution, but found that using http://chronosbox.org/blog/jsonresponse-in-django?lang=en was the source for duplicating my object instances. Weird as I've used this module before... but it seems to not be working nicely here. – Daniel Nill Dec 28 '11 at 03:33

1 Answers1

3

First of all, you should never use something like that to create a path :

'/'.join(['content/revisions', filename])

but :

os.path.join("my_dir", "my_subdir", ..., "filename.txt")

You are not supposed to know if your application runs on Unix-like or on Windows (yes, some people use Windows as webserver).

Also, you should not call your FileField attribute path, this is ambiguous with FilePathField.

Is this field NOT NULL ? Because in your create() statement you don't provide one. This should raise an Error.

I don't get this :

revision.path.save(basename, ContentFile(request.GET['revision']))

What are you trying to achieve ? Are you sure you want to store a GET parameter in the file ?

To answer your question, by default, Django does not take the responsability to overwrite a file that exists on your filesystem, this is why it automatically store it with an unique path by adding a suffix.

If this behaviour does not fits, consider writing a custom file storage.

Stan
  • 8,710
  • 2
  • 29
  • 31
  • To be clear it's creating two instances of the class Revision both with their own files. I guess I understand why ContentFile() would create a file and then `revision.path.save()` would create a file, but I don't see where it is making a second revision object. – Daniel Nill Dec 27 '11 at 22:55
  • Because if you call `MyModel.objects.create()` two times, you creates two entries in your database ! You need to use `get_or_create()` to retrieve or create a record. `ContentFile` does not create files (on the filesystem), this is more like a `StringIO`, in memory. – Stan Dec 27 '11 at 23:07
  • Sorry if I'm being dense but I still don't see it. I call `Revision.objects.create()` once and call `instance.file_field.save()` once. Any chance you could show me a working example or some sudo code? I don't see where or why I would use `get_or_create`. See http://groups.google.com/group/django-users/browse_frm/thread/184e5e09db1efce4/7816cbc650d8dc77 for the basic reference I'm working from. – Daniel Nill Dec 27 '11 at 23:16