Работа с файлами с помощью django-proxy-storage

Работа с файлами с помощью django-proxy-storage

Геннадий Чибисов (Яндекс)

В этом докладе я хочу рассказать каким образом с помощью нашего инструмента django-proxy-storage можно организовать:

Авторизацию раздачи файлов в веб-приложениях;
Динамическое использование нескольких стораджей;
Автоматический фолбэк до работающего стораджа на уровне приложения.

53b0434aded1fb944ec3037c382158c1?s=128

Moscow Python Meetup

October 01, 2014
Tweet

Transcript

  1. Django-proxy-storage Chibisov Gennady

  2. bit.ly/dps-doc 2

  3. Plan 1. What is django storage? 2. What is django-proxy-storage?

    3. Use cases of django-proxy-storage 3
  4. Simple usage with models class Message(models.Model):! user = models.ForeignKey(User)! to_user

    = models.ForeignKey(User)! attach = models.FileField(! storage=FileSystemStorage(! location="/media/"! )! )! 4
  5. Simple usage with models class Message(models.Model):! user = models.ForeignKey(User)! to_user

    = models.ForeignKey(User)! attach = models.FileField(! storage=FileSystemStorage(! location="/media/"! )! )! 5
  6. Simple usage with models class Message(models.Model):! user = models.ForeignKey(User)! to_user

    = models.ForeignKey(User)! attach = models.FileField(! storage=FileSystemStorage(! location="/media/"! )! )! 6
  7. Simple usage with models class Message(models.Model):! user = models.ForeignKey(User)! to_user

    = models.ForeignKey(User)! attach = models.FileField(! storage=FileSystemStorage(! location="/media/"! )! )! 7
  8. Simple usage with models >>> m = Message.objects.create(! >>> user=messi,!

    >>> user_to=ronaldo,! >>> )! >>> m.attach.save(open("om.png"))! "om.png" 8
  9. /media/om.png 9

  10. Django-storages - S3BotoStorage - LibCloudStorage - AzureStorage - GridFSStorage -

    DatabaseStorage 10
  11. Storage interface 11 path(name)

  12. Storage interface 12 open(name,mode)

  13. Storage interface 13 save(name,content)

  14. Storage interface 14 delete(name)

  15. Storage interface 15 exists(name)

  16. That’s it (almost) storage.path(…)! storage.open(…)! storage.save(…)! storage.delete(…)! storage.exists(…) 16

  17. Save example >>> om = open("om.png")! >>> st = FileSystemStorage(!

    >>> location="/media/"! >>> ) 17
  18. Save example >>> om = open("om.png")! >>> st = FileSystemStorage(!

    >>> location="/media/"! >>> ) 18
  19. Save example >>> om = open("om.png")! >>> st = FileSystemStorage(!

    >>> location="/media/"! >>> )! 19
  20. Save example >>> om = open("om.png")! >>> st = FileSystemStorage(!

    >>> location="/media/"! >>> )! >>> st.save(! >>> name='om.png'! >>> content=om! >>> )! "om.png"! 20
  21. Other methods examples >>> st.path("om.png")! "/media/om.png"! ! >>> st.exists("om.png")! True!

    ! >>> st.open("om.png")! <File: /media/om.png>! ! >>> st.delete("om.png")! 21
  22. Other methods examples >>> st.path("om.png")! "/media/om.png"! ! >>> st.exists("om.png")! True!

    ! >>> st.open("om.png")! <File: /media/om.png>! ! >>> st.delete("om.png")! 22
  23. Other methods examples >>> st.path("om.png")! "/media/om.png"! ! >>> st.exists("om.png")! True!

    ! >>> st.open("om.png")! <File: /media/om.png>! ! >>> st.delete("om.png")! 23
  24. Other methods examples >>> st.path("om.png")! "/media/om.png"! ! >>> st.exists("om.png")! True!

    ! >>> st.open("om.png")! <File: /media/om.png>! ! >>> st.delete("om.png")! 24
  25. django-proxy-storage 25

  26. 3 building blocks 26

  27. 1. It’s django storage 27

  28. 2. Original storage 28

  29. 2. Original storage 29 FileSystemStorage

  30. 2. Original storage 30 FileSystemStorage S3BotoStorage

  31. 2. Original storage 31 FileSystemStorage S3BotoStorage GridFSStorage

  32. 2. Original storage 32 FileSystemStorage S3BotoStorage GridFSStorage Any storage!

  33. 3. Meta-backend 33

  34. 3. Meta-backend 34 MongoDB, ORM

  35. 3. Meta-backend 35 MongoDB, ORM

  36. Proxy-storage example class MyStorage(ProxyStorageBase):! original_storage = \! FileSystemStorage(! location="/media/"! )!

    meta_backend = MongoMetaBackend(! database=get_mongo_db(),! collection="files_meta"! ) 36
  37. Proxy-storage base class class MyStorage(ProxyStorageBase):! original_storage = \! FileSystemStorage(! location="/media/"!

    )! meta_backend = MongoMetaBackend(! database=get_mongo_db(),! collection="files_meta"! ) 37
  38. Original storage class MyStorage(ProxyStorageBase):! original_storage = \! FileSystemStorage(! location="/media/"! )!

    meta_backend = MongoMetaBackend(! database=get_mongo_db(),! collection="files_meta"! ) 38
  39. Original storage class MyStorage(ProxyStorageBase):! original_storage = \! FileSystemStorage(! location="/media/"! )!

    meta_backend = MongoMetaBackend(! database=get_mongo_db(),! collection="files_meta"! ) 39
  40. Meta-backend class MyStorage(ProxyStorageBase):! original_storage = \! FileSystemStorage(! location="/media/"! )! meta_backend

    = MongoMetaBackend(! database=get_mongo_db(),! collection="files_meta"! ) 40
  41. Meta-backend class MyStorage(ProxyStorageBase):! original_storage = \! FileSystemStorage(! location="/media/"! )! meta_backend

    = MongoMetaBackend(! database=get_mongo_db(),! collection="files_meta"! ) 41
  42. nom.png 42

  43. Proxy-storage save >>> nom = open("nom.png")! >>> st = MyStorage()

    43
  44. Proxy-storage save >>> nom = open("nom.png")! >>> st = MyStorage()

    44
  45. Proxy-storage save >>> nom = open("nom.png")! >>> st = MyStorage()!

    ! >>> st.save(! >>> name="nom.png"! >>> content=nom! >>> ) 45
  46. Proxy-storage save >>> nom = open("nom.png")! >>> st = MyStorage()!

    ! >>> st.save(! >>> name="nom.png"! >>> content=nom! >>> )! "/media/nom.png" 46
  47. How save works 1. Save file to original storage (file

    system) 47
  48. How save works 1. Save file to original storage (file

    system) 2. Save file info to meta-backend (mongoDB) 48
  49. File info in meta-backend {! "_id": ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",!

    "original_storage_path":"nom.png"! } 49
  50. File info in meta-backend {! "_id": ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",!

    "original_storage_path":"nom.png"! } 50
  51. File info in meta-backend {! "_id": ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",!

    "original_storage_path":"nom.png"! } 51
  52. File info in meta-backend {! "_id": ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",!

    "original_storage_path":"nom.png"! } 52
  53. Proxy-storage open >>> st.open("/media/nom.png")! <File: /media/nom.png> 53

  54. Find meta-info by path >>> st.open("/media/nom.png")! <File: /media/nom.png>! ! {!

    "_id": ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! } 54
  55. Return original’s storage open >>> st.open("/media/nom.png")! <File: /media/nom.png>! ! {!

    "_id": ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! } 55
  56. Proxy-storage exists >>> st.exists("/media/nom.png")! True 56

  57. Find meta-info by path >>> st.exists("/media/nom.png")! True! ! {! "_id":

    ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! } 57
  58. Return found or not >>> st.exists("/media/nom.png")! True! ! {! "_id":

    ObjectId("..."),! "path": "/media/nom.png",! "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! } 58
  59. Proxy-storage delete >>> st.delete("/media/nom.png")! 59

  60. Find meta-info by path >>> st.delete("/media/nom.png")! ! {! "_id": ObjectId("..."),!

    "path": "/media/nom.png",! "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! }! 60
  61. Call original’s storage delete >>> st.delete("/media/nom.png")! ! {! "_id": ObjectId("..."),!

    "path": "/media/nom.png",! "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! }! 61
  62. Remove meta-info >>> st.delete("/media/nom.png")! ! {! "_id": ObjectId("..."),! "path": "/media/nom.png",!

    "proxy_storage_name":"my_storage",! "original_storage_path":"nom.png"! }! 62
  63. Use cases 63

  64. Custom data for meta-backend class MyStorage(ProxyStorageBase):! ...! def get_data_for_meta_backend_save(! self,

    content, **kwargs! ):! data = super...! data["size"] = get_size(content)! data["mime"] = get_mime(content)! return data 64
  65. Custom data for meta-backend class MyStorage(ProxyStorageBase):! ...! def get_data_for_meta_backend_save(! self,

    content, **kwargs! ):! data = super...! data["size"] = get_size(content)! data["mime"] = get_mime(content)! return data 65
  66. Custom data for meta-backend class MyStorage(ProxyStorageBase):! ...! def get_data_for_meta_backend_save(! self,

    content, **kwargs! ):! data = super...! data["size"] = get_size(content)! data["mime"] = get_mime(content)! return data 66
  67. Custom data for meta-backend {! "_id": ObjectId("..."),! "proxy_storage_name":"my_storage",! "path": "/media/nom.png",!

    "original_storage_path":"nom.png",! "size": 70903,! "mime": "image/png",! } 67
  68. Multiple original storages 68

  69. Multiple original storages class MyStorage(! MultipleOriginalStoragesMixin, ! ProxyStorageBase! ):! ...!

    original_storages = (! ("fs", FileSystemStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 69
  70. Multiple original storages class MyStorage(! MultipleOriginalStoragesMixin, ! ProxyStorageBase! ):! ...!

    original_storages = (! ("fs", FileSystemStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 70
  71. Multiple original storages class MyStorage(! MultipleOriginalStoragesMixin, ! ProxyStorageBase! ):! ...!

    original_storages = (! ("fs", FileSystemStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 71
  72. Multiple original storages class MyStorage(! MultipleOriginalStoragesMixin, ! ProxyStorageBase! ):! ...!

    original_storages = (! ("fs", FileSystemStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 72
  73. Multiple original storages >>> st.save(! >>> name="om.png"! >>> content=om,! >>>

    using="fs"! >>> ) 73
  74. Multiple original storages >>> st.save(! >>> name="om.png"! >>> content=om,! >>>

    using="fs"! >>> ) 74
  75. Multiple original storages {! "_id": ObjectId("..."),! "proxy_storage_name":"my_storage",! "path": "/media/om.png",! "original_storage_path":"om.png",!

    "original_storage_name":"fs"! } 75
  76. Multiple original storages >>> st.save(! >>> name="nom.png"! >>> content=nom,! >>>

    using="amazon"! >>> ) 76
  77. Multiple original storages {! "_id": ObjectId("..."),! "proxy_storage_name":"my_storage",! "path":"http://am.com/nom.png”,! "original_storage_path":"nom.png",! "original_storage_name":"amazon"!

    } 77
  78. Multiple original storages >>> st.open("/media/om.png") From FS 78

  79. Multiple original storages >>> st.open(! >>> "http://am.com/nom.png") From amazon 79

  80. Save text files to FS by default class MyStorage(...):! ...!

    def save(self, **kw):! if not using:! if is_txt(kw["name"]):! using = "fs"! else:! using = "amazon"! return super(...).save(**kw) 80
  81. Save text files to FS by default class MyStorage(...):! ...!

    def save(self, **kw):! if not using:! if is_txt(kw["name"]):! using = "fs"! else:! using = "amazon"! return super(...).save(**kw) 81
  82. Save text files to FS by default class MyStorage(...):! ...!

    def save(self, **kw):! if not using:! if is_txt(kw["name"]):! using = "fs"! else:! using = "amazon"! return super(...).save(**kw) 82
  83. Save text files to FS by default class MyStorage(...):! ...!

    def save(self, **kw):! if not using:! if is_txt(kw["name"]):! using = "fs"! else:! using = "amazon"! return super(...).save(**kw) 83
  84. Save text files to FS by default class MyStorage(...):! ...!

    def save(self, **kw):! if not using:! if is_txt(kw["name"]):! using = "fs"! else:! using = "amazon"! return super(...).save(**kw) 84
  85. Fallback 85

  86. Fallback class MyStorage(! FallbackProxyStorageMixin, ! ProxyStorageBase! ):! ...! original_storages =

    (! ("fs", OrigFSStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 86
  87. Fallback class MyStorage(! FallbackProxyStorageMixin, ! ProxyStorageBase! ):! ...! original_storages =

    (! ("fs", OrigFSStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 87
  88. Fallback class MyStorage(! FallbackProxyStorageMixin, ! ProxyStorageBase! ):! ...! original_storages =

    (! ("fs", OrigFSStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 88
  89. Fallback class MyStorage(! FallbackProxyStorageMixin, ! ProxyStorageBase! ):! ...! original_storages =

    (! ("fs", OrigFSStorage(! location="/media/"),! ("amazon", AmazonS3Storage()),! ) 89
  90. Original storage for fallback class OrigFSStorage(! OriginalStorageFallbackMixin,! FileSystemStorage! ):! fallback_exceptions

    = (! IOError, ! OSError! ) 90
  91. Original storage for fallback class OrigFSStorage(! OriginalStorageFallbackMixin,! FileSystemStorage! ):! fallback_exceptions

    = (! IOError, ! OSError! ) 91
  92. Original storage for fallback class OrigFSStorage(! OriginalStorageFallbackMixin,! FileSystemStorage! ):! fallback_exceptions

    = (! IOError, ! OSError! ) 92
  93. Original storage for fallback class OrigFSStorage(! OriginalStorageFallbackMixin,! FileSystemStorage! ):! fallback_exceptions

    = (! IOError, ! OSError! ) 93
  94. Original storage for fallback class OrigFSStorage(! OriginalStorageFallbackMixin,! FileSystemStorage! ):! fallback_exceptions

    = (! IOError, ! OSError! ) 94
  95. Save without errors >>> st.save(! >>> name="om.png"! >>> content=om! >>>

    ) 95
  96. Save without errors {! "_id": ObjectId("..."),! "proxy_storage_name":"my_storage",! "path": "/media/om.png",! "original_storage_path":"om.png",!

    "original_storage_name":"fs"! } 96
  97. Let’s remove dir $ rm -rf /media/ 97

  98. Fallback >>> st.save(! >>> name="nom.png"! >>> content=nom,! >>> ) 98

  99. Fallback >>> st.save(! >>> name="nom.png"! >>> content=nom,! >>> )! IOError

    99
  100. Fallback In your face, FS! 100

  101. Fallback {! "_id": ObjectId("..."),! "proxy_storage_name":"my_storage",! "path":"http://am.com/nom.png”,! "original_storage_path":"nom.png",! "original_storage_name":"amazon"! } 101

  102. Authorization 102

  103. Simple model class Message(models.Model):! user = models.ForeignKey(User)! to_user = models.ForeignKey(User)!

    attach = models.FileField(! storage=FileSystemStorage(! location="/media/"! )! )! 103
  104. Nginx config without auth server {! server_name site.ru;! ! location

    / {! proxy_pass http://app.socket;! }! ! location /files/ {! alias /media/;! }! } 104
  105. Nginx config without auth server {! server_name site.ru;! ! location

    / {! proxy_pass http://app.socket;! }! ! location /files/ {! alias /media/;! }! } 105
  106. Nginx config without auth server {! server_name site.ru;! ! location

    / {! proxy_pass http://app.socket;! }! ! location /files/ {! alias /media/;! }! } 106
  107. Serve without auth 107 Client Nginx /files/

  108. Serve without auth 108 Client Nginx /files/

  109. Serve without auth 109 Client Nginx /files/

  110. Serve without auth 110 Client Nginx /files/

  111. Serve without auth 111 Client Nginx /files/

  112. Serve without auth 112 Client Nginx /files/

  113. http://site.ru/files/om.png 113

  114. Add auth logic Serve files only for author or receiver

    of message 114
  115. Nginx config without auth server {! server_name site.ru;! ! location

    / {! proxy_pass http://app.socket;! }! ! location /files/ {! alias /media/;! }! } 115
  116. Nginx config with auth server {! server_name site.ru;! ! location

    / {! proxy_pass http://app.socket;! }! ! location /files/ {! internal;! alias /media/;! }! } 116
  117. Client can’t directly get files 117 Client Nginx /files/

  118. Rename location server {! server_name site.ru;! ! location / {!

    proxy_pass http://app.socket;! }! ! location /x-files/ {! internal;! alias /media/;! }! } 118
  119. Auth logic in view urlpatterns = patterns('',! url(r'^files/$', file_view)! )

    119
  120. Auth logic in view def file_view(request):! path = request.GET["path"]! res

    = HttpResponse()! if is_has_access(request,path):! res["X-Accel-Redirect"] = \! "/x-files/" + path! else:! res.status_code = 404! return res 120
  121. Auth logic in view def file_view(request):! path = request.GET["path"]! res

    = HttpResponse()! if is_has_access(request,path):! res["X-Accel-Redirect"] = \! "/x-files/" + path! else:! res.status_code = 404! return res 121
  122. Auth logic in view def file_view(request):! path = request.GET["path"]! res

    = HttpResponse()! if is_has_access(request,path):! res["X-Accel-Redirect"] = \! "/x-files/" + path! else:! res.status_code = 404! return res 122
  123. Auth logic in view def file_view(request):! path = request.GET["path"]! res

    = HttpResponse()! if is_has_access(request,path):! res["X-Accel-Redirect"] = \! "/x-files/" + path! else:! res.status_code = 404! return res 123
  124. Auth logic in view def file_view(request):! path = request.GET["path"]! res

    = HttpResponse()! if is_has_access(request,path):! res["X-Accel-Redirect"] = \! "/x-files/" + path! else:! res.status_code = 404! return res 124
  125. Serve with auth Client / /x-files/ Django app Nginx 125

  126. Serve with auth Client / /x-files/ Django app Nginx 126

  127. Serve with auth Client / /x-files/ Django app Nginx 127

  128. Serve with auth Client / /x-files/ Django app Nginx 128

  129. Serve with auth Client / /x-files/ Django app Nginx X-Accel-Redirect:

    /x-files/om.png 129
  130. Serve with auth Client / /x-files/ Django app Nginx X-Accel-Redirect:

    /x-files/om.png 130
  131. Serve with auth Client / /x-files/ Django app Nginx X-Accel-Redirect:

    /x-files/om.png 131
  132. Serve with auth Client / /x-files/ Django app Nginx X-Accel-Redirect:

    /x-files/om.png 132
  133. Serve with auth Client / /x-files/ Django app Nginx X-Accel-Redirect:

    /x-files/om.png 133
  134. Serve with auth Client / /x-files/ Django app Nginx X-Accel-Redirect:

    /x-files/om.png 134
  135. http://site.ru/files/?path=om.png 135

  136. Auth logic in view def file_view(request):! path = request.GET["path"]! res

    = HttpResponse()! if is_has_access(request,path):! res["X-Accel-Redirect"] = \! "/x-files/" + path! else:! res.status_code = 404! return res 136
  137. Find message with path def is_has_access(request, path):! m = Message.objects.get(!

    attach=path! )! ... 137
  138. What if user has resume? class Resume(models.Model):! user = models.ForeignKey(User)!

    file = models.FileField(! storage=FileSystemStorage(! location="/resumes/"! )! )! ! # Show resume only for owners! 138
  139. Find message or resume by path def is_has_access(request, path):! m

    = Message.objects.get(! attach=path! )! res = Resume.objects.get(! file=path! )! ... 139
  140. 3 Problems 1. Collision of paths. /media/om.png and /resumes/om.png !

    140
  141. 3 Problems 1. Collision of paths. /media/om.png and /resumes/om.png 2.

    N requests for N models 141
  142. 3 Problems 1. Collision of paths. /media/om.png and /resumes/om.png 2.

    N requests for N models 3. Context of field 142
  143. Refactor to proxy-storage class Message(models.Model):! user = models.ForeignKey(User)! to_user =

    models.ForeignKey(User)! attach = models.FileField(! storage=FileSystemStorage(! location="/media/"! )! )! 143
  144. Refactor to proxy-storage class Message(models.Model):! user = models.ForeignKey(User)! to_user =

    models.ForeignKey(User)! attach = models.FileField(! storage=AttStorage()! )! 144
  145. Refactor to proxy-storage class Resume(models.Model):! user = models.ForeignKey(User)! file =

    models.FileField(! storage=FileSystemStorage(! location="/resumes/"! )! )! 145
  146. Refactor to proxy-storage class Resume(models.Model):! user = models.ForeignKey(User)! file =

    models.FileField(! storage=ResStorage()! )! 146
  147. Proxy-storages mongo_meta = MongoMetaBackend(…)! ! class AttStorage(ProxyStorageBase): original_storage = \!

    FSStorage(location="/media/")! meta_backend = mongo_meta! ! class ResStorage(ProxyStorageBase): original_storage = \! FSStorage(location="/resumes/")! meta_backend = mongo_meta! 147
  148. Proxy-storages mongo_meta = MongoMetaBackend(…)! ! class AttStorage(ProxyStorageBase): original_storage = \!

    FSStorage(location="/media/")! meta_backend = mongo_meta! ! class ResStorage(ProxyStorageBase): original_storage = \! FSStorage(location="/resumes/")! meta_backend = mongo_meta! 148
  149. Path is unique per meta-backend {! "_id": ObjectId("..."),! "path": "/media/om.png",!

    "proxy_storage_name":"attachment",! "original_storage_path":"om.png"! },! {! "_id": ObjectId("..."),! "path": "/resumes/om.png",! "proxy_storage_name":"resumes",! "original_storage_path":"om.png"! }! 149
  150. Has the same original path {! "_id": ObjectId("..."),! "path": "/media/om.png",!

    "proxy_storage_name":"attachment",! "original_storage_path":"om.png"! },! {! "_id": ObjectId("..."),! "path": "/resumes/om.png",! "proxy_storage_name":"resumes",! "original_storage_path":"om.png"! }! 150
  151. Path is unique per meta-backend {! "_id": ObjectId("..."),! "path": "/media/om.png",!

    "proxy_storage_name":"attachment",! "original_storage_path":"om.png"! },! {! "_id": ObjectId("..."),! "path": "/resumes/om.png",! "proxy_storage_name":"resumes",! "original_storage_path":"om.png"! }! 151
  152. 2 Problems 1. Collision of paths. /media/om.png and /resumes/om.png 2.

    N requests for N models 3. Context of field 152
  153. Refactor to ProxyStorageFileField class Message(models.Model):! user = models.ForeignKey(User)! to_user =

    models.ForeignKey(User)! attach = models.FileField(! storage=AttStorage()! )! 153
  154. Refactor to ProxyStorageFileField class Message(models.Model):! user = models.ForeignKey(User)! to_user =

    models.ForeignKey(User)! attach = ProxyStorageFileField(! storage=AttStorage()! )! 154
  155. Refactor to ProxyStorageFileField class Resume(models.Model):! user = models.ForeignKey(User)! file =

    models.FileField(! storage=ResStorage()! )! 155
  156. Refactor to ProxyStorageFileField class Resume(models.Model):! user = models.ForeignKey(User)! file =

    ProxyStorageFileField(! storage=ResStorage()! )! 156
  157. ProxyStorageFileField new data {! "_id": ObjectId("..."),! "path": "/media/om.png",! "proxy_storage_name":"attachment",! "original_storage_path":"om.png",!

    "content_type_id": 1,! "object_id": 100,! "field": "attach"! } 157
  158. ProxyStorageFileField new data {! "_id": ObjectId("..."),! "path": "/media/om.png",! "proxy_storage_name":"attachment",! "original_storage_path":"om.png",!

    "content_type_id": 1,! "object_id": 100,! "field": "attach"! } 158
  159. ProxyStorageFileField new data {! "_id": ObjectId("..."),! "path": "/media/om.png",! "proxy_storage_name":"attachment",! "original_storage_path":"om.png",!

    "content_type_id": 1,! "object_id": 100,! "field": "attach"! } 159
  160. No problems 1. Collision of paths. /media/om.png and /resumes/om.png 2.

    N requests for N models 3. Context of field 160
  161. Find model instance from data def is_has_access(request, path):! data =

    mongo_meta.get(path)! model = ContentType.objects.get(! id=data["content_type_id"]! )! instance = model.objects.get(! id=data["object_id"]! )! return instance.check_access(! request, data["field"]) 161
  162. Find model instance from data def is_has_access(request, path):! data =

    mongo_meta.get(path)! model = ContentType.objects.get(! id=data["content_type_id"]! )! instance = model.objects.get(! id=data["object_id"]! )! return instance.check_access(! request, data["field"]) 162
  163. Find model instance from data def is_has_access(request, path):! data =

    mongo_meta.get(path)! model = ContentType.objects.get(! id=data["content_type_id"]! )! instance = model.objects.get(! id=data["object_id"]! )! return instance.check_access(! request, data["field"]) 163
  164. Find model instance from data def is_has_access(request, path):! data =

    mongo_meta.get(path)! model = ContentType.objects.get(! id=data["content_type_id"]! )! instance = model.objects.get(! id=data["object_id"]! )! return instance.check_access(! request, data["field"]) 164
  165. Find model instance from data def is_has_access(request, path):! data =

    mongo_meta.get(path)! model = ContentType.objects.get(! id=data["content_type_id"]! )! instance = model.objects.get(! id=data["object_id"]! )! return instance.check_access(! request, data["field"]) 165
  166. Message check_access class Message(models.Model):! ...! def check_access(self,req,field):! if field ==

    "attach":! return req.user in (! self.user, ! self.to_user! ) 166
  167. Message check_access class Message(models.Model):! ...! def check_access(self,req,field):! if field ==

    "attach":! return req.user in (! self.user, ! self.to_user! ) 167
  168. Resume check_access class Resume(models.Model):! ...! def check_access(self,req,field):! if field ==

    "file":! return req.user == self.user 168
  169. Resume check_access class Resume(models.Model):! ...! def check_access(self,req,field):! if field ==

    "file":! return req.user == self.user 169
  170. Make alias to root server {! server_name site.ru;! ! location

    / {! proxy_pass http://app.socket;! }! ! location /x-files/ {! internal;! alias /;! }! } 170
  171. Not resume owner Client / /x-files/ Django app Nginx 404

    171
  172. /files/?path=/resumes/om.png 172 404

  173. Resume owner Client / /x-files/ Django app Nginx X-Accel-Redirect: !

    /x-files/resumes/om.png 173
  174. /files/?path=/resumes/om.png 174

  175. True story class MainStorage(! FallbackProxyStorageMixin, ProxyStorageBase):! original_storages = [! ('elliptics',

    OrigElliptics()),! ('gridfs', GridFSStorage()),! ]! meta_backend = ORMMetaBackend(! model=ProxyStorageModel! ) 175
  176. True story class MainStorage(! FallbackProxyStorageMixin, ProxyStorageBase):! original_storages = [! ('elliptics',

    OrigElliptics()),! ('gridfs', GridFSStorage()),! ]! meta_backend = ORMMetaBackend(! model=ProxyStorageModel! ) 176
  177. True story class MainStorage(! FallbackProxyStorageMixin, ProxyStorageBase):! original_storages = [! ('elliptics',

    OrigElliptics()),! ('gridfs', GridFSStorage()),! ]! meta_backend = ORMMetaBackend(! model=ProxyStorageModel! ) 177
  178. True story from requests.exceptions import \ RequestException! ! class OrigElliptics(!

    OriginalStorageFallbackMixin, EllipticsStorage! ):! fallback_exceptions = (! RequestException,! ) 178
  179. True story from requests.exceptions import \ RequestException! ! class OrigElliptics(!

    OriginalStorageFallbackMixin, EllipticsStorage! ):! fallback_exceptions = (! RequestException,! ) 179
  180. 180

  181. 181

  182. X-Accel-Redirect: /Спасибо/ Repo - https://github.com/chibisov/django-proxy-storage Docs - http://chibisov.github.io/django-proxy-storage/docs/ https://github.com/chibisov @MyNameIss

    This fellow is from ! http://www.cuttherope.net/