On Wed, Jun 23, 2010 at 2:58 AM, Russell Keith-Magee <russ...@keith-magee.com> wrote: > On Wed, Jun 23, 2010 at 2:58 AM, Waldemar Kornewald > <wkornew...@gmail.com> wrote: >> On Tue, Jun 22, 2010 at 2:40 PM, Russell Keith-Magee >> <russ...@keith-magee.com> wrote: >>> On Tue, Jun 22, 2010 at 2:55 PM, Waldemar Kornewald >>> <wkornew...@gmail.com> wrote: >>> It also strikes me that a lot of this is being configured at the >>> global level -- i.e., you have to nominate that your public upload >>> backend will be S3, rather than nominating that a specific file field >>> will be backed by S3. >> >> I'm repeating myself here, but anyway: The primary purpose of this API >> is to allow for writing reusable Django apps that automatically work >> with your project's file handling solution(s) without hard-coding >> anything. This requires that you specify the upload/download backends >> separately from FileField (otherwise it's hard-coded and not reusable) >> and instead provide a mechanism for detecting which backend should be >> chosen for the current request. Of course, this mechanism could take >> the FileField into account. > > You may feel that you're repeating yourself, but this point certainly > wasn't clear to me from your previous two posts. > > Making it possible to configure the file handling strategy of a > reusable app is certainly a reasonable feature request. However: > > 1) Again, the file storage strategy isn't something that is constant > across a deployment. I may want to use S3 for one type of file, but > use local storage for another. > > 2) Handling this flexibility at the level of the request is > completely the wrong approach. File storage doesn't change per request > -- it's defined per model. Every usage of the Profile model has an > 'avatar' ImageField; it doesn't matter how or where you access that > field, it needs to be accessed and displayed the same way. This is a > per-model setting, not a per-view or per-request setting.
That's ok, but in addition to the model and field I'd still pass the request object to the backend, so you can check if the user hasn't used up his quote or maybe other things. > There is a larger issue here of how we should treat the problem of > configuring the internals of reusable applications. It's analogous to > the reasons why we dropped the 'using' argument from Meta declarations > on models -- it isn't a decision that a reusable-app-maker can make; > it needs to be configured as a deployment decision. > > The solution for 'using' was to introduce the idea of a database > Router; perhaps an analogous approach is required here. The "delegate" backend is basically a router. With the django-filetransfers API you can just write another backend which does the routing. >> Don't worry about the current code. We haven't yet officially >> announced that project and I'll hold it back until it's clear whether >> this will be part of Django core or not. We can completely reinvent >> the API from scratch if necessary. > > In which, case, you need to make a specific proposal. I'll admit that > this is a problem that needs to be addressed, and I'm interested in > seeing approaches that addressing this problem, I can't say it's a > particularly high priority for me. I'm happy to give you feedback on a > specific proposal, but I have many much higher priority items on my > plate for 1.3. OK, so before I send a patch, here is how I'd like to do it: >From the end-user perspective ----------------------------------------------- FileField gets a new method prepare_upload() which takes the following arguments: * request * upload_url: the target URL of the upload view * private: should this be only privately accessibly or also publicly? (default: False; whether this actually works depends on the chosen backend's capabilities and your hosting setup) The function returns a tuple with a new target submission URL and a dict with additional POST data. forms.Form also gets a prepare_upload() function which searches for a FileField. If multiple FileFields exist and the backend doesn't support multi-file uploads an exception is raised. The function doesn't return anything, but instead stores the results as self.upload_url and self.some_file_field.post_data. When the form is rendered the FileField automatically generates <input type="hidden" /> fields for self.post_data. Q: or should prepare_upload() better be defined on forms.FileField? File (from FileField) gets a public_download_url() method which takes no arguments. It returns the file's permanent public URL or None if no such URL exists. File (from FileField) gets a serve() method which takes the following arguments: * request * save_as: if False (default) lets the browser decide whether to display or download the file; if True forces browser to download as "file.name"; if it's a string it forces a download with the given string as the file name * content_type: if None (default) automatically detects content type based on file name; otherwise it overrides the default content type This returns an HttpResponse. Example: Upload view: form = FileForm() form.prepare_upload(request, '/upload') Upload template: <form action="{{ form.upload_url }}" ...> {% csrf_token %} <table>{{ form }}</table> </form> Download template for private-only downloads (entity is a model instance): <a href="{% url download_view pk=entity.pk %}">Download</a> Download template for public downloads (entity is a model instance with a FileField called "file"): {% url download_view pk=entity.pk as fallback_url %} <a href="{% firstof entity.file.public_download_url fallback_url %}">Download</a> Download view for public and private downloads (public only as a fall-back if there is no public_download_url): entity.file.serve(request) The prepare_upload(), public_download_url(), and serve() functions each have their own backend type. Each of the three backend types is configured separately in settings.py: PREPARE_UPLOAD_BACKEND = { 'backend': 'some.backend', 'setting': ..., 'setting2': ..., } PUBLIC_DOWNLOAD_URL_BACKEND = { 'backend': 'some.backend', ... } SERVE_FILE_BACKEND = { 'backend': 'some.backend', ... } Backend API -------------------------------------------------------- I won't, yet talk too much about the backend API itself because we should first talk about the end-user API. Each backend type gets all of the parameters that the user passes to the respective public function (prepare_upload(), etc.). Additionally, each function gets the model and FileField. Also, the File methods (public_download_url() and serve()) get the file instance: def prepare_upload(model, field, request, upload_url, private, **kwargs): ... def public_download_url(model, field, file, **kwargs): ... def serve_file(model, field, file, request, save_as, content_type, **kwargs): ... There is no special Router API. Instead, you can write your own backend which routes/delegates to some other backend. Bye, Waldemar Kornewald -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.