|
1420
|
1 |
# -*- coding: utf-8 -*- |
|
|
2 |
''' |
|
|
3 |
Created on Sep 1, 2015 |
|
|
4 |
|
|
|
5 |
@author: ymh |
|
|
6 |
''' |
|
|
7 |
|
|
|
8 |
from django.apps import AppConfig |
|
|
9 |
from django.core.files.base import File |
|
|
10 |
from django.db.models.fields.files import FieldFile |
|
|
11 |
from django.db.models.fields.files import FileDescriptor |
|
|
12 |
import six |
|
|
13 |
|
|
|
14 |
|
|
|
15 |
def ldt_get(self, instance=None, owner=None): |
|
|
16 |
if instance is None: |
|
|
17 |
raise AttributeError( |
|
|
18 |
"The '%s' attribute can only be accessed from %s instances." |
|
|
19 |
% (self.field.name, owner.__name__)) |
|
|
20 |
|
|
|
21 |
# This is slightly complicated, so worth an explanation. |
|
|
22 |
# instance.file`needs to ultimately return some instance of `File`, |
|
|
23 |
# probably a subclass. Additionally, this returned object needs to have |
|
|
24 |
# the FieldFile API so that users can easily do things like |
|
|
25 |
# instance.file.path and have that delegated to the file storage engine. |
|
|
26 |
# Easy enough if we're strict about assignment in __set__, but if you |
|
|
27 |
# peek below you can see that we're not. So depending on the current |
|
|
28 |
# value of the field we have to dynamically construct some sort of |
|
|
29 |
# "thing" to return. |
|
|
30 |
|
|
|
31 |
# The instance dict contains whatever was originally assigned |
|
|
32 |
# in __set__. |
|
|
33 |
file = instance.__dict__[self.field.name] # @ReservedAssignment |
|
|
34 |
|
|
|
35 |
# If this value is callable (certainly because the field was defined |
|
|
36 |
# with a callable default), we execute the function and assign the |
|
|
37 |
# result to the value |
|
|
38 |
if six.callable(file): |
|
|
39 |
file = file() # @ReservedAssignment |
|
|
40 |
if isinstance(file, FieldFile) and not hasattr(file, 'field'): |
|
|
41 |
instance.__dict__[self.field.name] = file |
|
|
42 |
|
|
|
43 |
# If this value is a string (instance.file = "path/to/file") or None |
|
|
44 |
# then we simply wrap it with the appropriate attribute class according |
|
|
45 |
# to the file field. [This is FieldFile for FileFields and |
|
|
46 |
# ImageFieldFile for ImageFields; it's also conceivable that user |
|
|
47 |
# subclasses might also want to subclass the attribute class]. This |
|
|
48 |
# object understands how to convert a path to a file, and also how to |
|
|
49 |
# handle None. |
|
|
50 |
if isinstance(file, six.string_types) or file is None: |
|
|
51 |
attr = self.field.attr_class(instance, self.field, file) |
|
|
52 |
instance.__dict__[self.field.name] = attr |
|
|
53 |
|
|
|
54 |
# Other types of files may be assigned as well, but they need to have |
|
|
55 |
# the FieldFile interface added to them. Thus, we wrap any other type of |
|
|
56 |
# File inside a FieldFile (well, the field's attr_class, which is |
|
|
57 |
# usually FieldFile). |
|
|
58 |
elif isinstance(file, File) and not isinstance(file, FieldFile): |
|
|
59 |
file_copy = self.field.attr_class(instance, self.field, file.name) |
|
|
60 |
file_copy.file = file |
|
|
61 |
file_copy._committed = False |
|
|
62 |
instance.__dict__[self.field.name] = file_copy |
|
|
63 |
|
|
|
64 |
# Finally, because of the (some would say boneheaded) way pickle works, |
|
|
65 |
# the underlying FieldFile might not actually itself have an associated |
|
|
66 |
# file. So we need to reset the details of the FieldFile in those cases. |
|
|
67 |
elif isinstance(file, FieldFile) and not hasattr(file, 'field'): |
|
|
68 |
file.instance = instance |
|
|
69 |
file.field = self.field |
|
|
70 |
file.storage = self.field.storage |
|
|
71 |
|
|
|
72 |
# That was fun, wasn't it? |
|
|
73 |
return instance.__dict__[self.field.name] |
|
|
74 |
|
|
|
75 |
class LdtAppConfig(AppConfig): |
|
|
76 |
''' |
|
|
77 |
App config to monkey patch django.db.models.fields.files.FileDescriptor.__get__ (cf django bug #24823 https://code.djangoproject.com/ticket/24823) |
|
|
78 |
''' |
|
|
79 |
|
|
|
80 |
name = 'ldt' |
|
|
81 |
verbose_name = 'LDT Platform' |
|
|
82 |
|
|
|
83 |
def ready(self): |
|
|
84 |
FileDescriptor.__get__ = ldt_get |