1 Storing images on the file-system |
|
2 --------------------------------- |
|
3 |
|
4 Step 1: configuring the BytesFileSystem storage |
|
5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
6 |
|
7 To avoid cluttering my database, and to ease file manipulation, I don't want them |
|
8 to be stored in the database. I want to be able create File entities for some |
|
9 files on the server file system, where those file will be accessed to get |
|
10 entities data. To do so I've to set a custom :class:`BytesFileSystemStorage` |
|
11 storage for the File 'data' attribute, which hold the actual file's content. |
|
12 |
|
13 Since the function to register a custom storage needs to have a repository |
|
14 instance as first argument, we've to call it in a server startup hook. So I added |
|
15 in `cubes/sytweb/hooks.py` : |
|
16 |
|
17 .. sourcecode:: python |
|
18 |
|
19 from os import makedirs |
|
20 from os.path import join, exists |
|
21 |
|
22 from cubicweb.server import hook |
|
23 from cubicweb.server.sources import storages |
|
24 |
|
25 class ServerStartupHook(hook.Hook): |
|
26 __regid__ = 'sytweb.serverstartup' |
|
27 events = ('server_startup', 'server_maintenance') |
|
28 |
|
29 def __call__(self): |
|
30 bfssdir = join(self.repo.config.appdatahome, 'bfss') |
|
31 if not exists(bfssdir): |
|
32 makedirs(bfssdir) |
|
33 print 'created', bfssdir |
|
34 storage = storages.BytesFileSystemStorage(bfssdir) |
|
35 storages.set_attribute_storage(self.repo, 'File', 'data', storage) |
|
36 |
|
37 .. Note:: |
|
38 |
|
39 * how we built the hook's registry identifier (`__regid__`): you can introduce |
|
40 'namespaces' by using there python module like naming identifiers. This is |
|
41 especially important for hooks where you usually want a new custom hook, not |
|
42 overriding / specializing an existant one, but the concept may be applied to |
|
43 any application objects |
|
44 |
|
45 * we catch two events here: "server_startup" and "server_maintenance". The first |
|
46 is called on regular repository startup (eg, as a server), the other for |
|
47 maintenance task such as shell or upgrade. In both cases, we need to have |
|
48 the storage set, else we'll be in trouble... |
|
49 |
|
50 * the path given to the storage is the place where file added through the ui |
|
51 (or in the database before migration) will be located |
|
52 |
|
53 * beware that by doing this, you can't anymore write queries that will try to |
|
54 restrict on File `data` attribute. Hopefuly we don't do that usually |
|
55 on file's content or more generally on attributes for the Bytes type |
|
56 |
|
57 Now, if you've already added some photos through the web ui, you'll have to |
|
58 migrate existing data so file's content will be stored on the file-system instead |
|
59 of the database. There is a migration command to do so, let's run it in the |
|
60 cubicweb shell (in real life, you would have to put it in a migration script as we |
|
61 have seen last time): |
|
62 |
|
63 :: |
|
64 |
|
65 $ cubicweb-ctl shell sytweb_instance |
|
66 entering the migration python shell |
|
67 just type migration commands or arbitrary python code and type ENTER to execute it |
|
68 type "exit" or Ctrl-D to quit the shell and resume operation |
|
69 >>> storage_changed('File', 'data') |
|
70 [........................] |
|
71 |
|
72 |
|
73 That's it. Now, files added through the web ui will have their content stored on |
|
74 the file-system, and you'll also be able to import files from the file-system as |
|
75 explained in the next part. |
|
76 |
|
77 Step 2: importing some data into the instance |
|
78 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
79 |
|
80 Hey, we start to have some nice features, let us give a try to this new web |
|
81 site. For instance if I have a 'photos/201005WePyrenees' containing pictures for |
|
82 a particular event, I can import it to my web site by typing :: |
|
83 |
|
84 $ cubicweb-ctl fsimport -F sytweb_instance photos/201005WePyrenees/ |
|
85 ** importing directory /home/syt/photos/201005WePyrenees |
|
86 importing IMG_8314.JPG |
|
87 importing IMG_8274.JPG |
|
88 importing IMG_8286.JPG |
|
89 importing IMG_8308.JPG |
|
90 importing IMG_8304.JPG |
|
91 |
|
92 .. Note:: |
|
93 The -F option means that folders should be mapped, hence my photos will be |
|
94 linked to a Folder entity corresponding to the file-system folder. |
|
95 |
|
96 Let's take a look at the web ui: |
|
97 |
|
98 .. image:: ../../images/tutos-photowebsite_ui1.png |
|
99 |
|
100 Nothing different, I can't see the new folder... But remember our security model! |
|
101 By default, files are only accessible to authenticated users, and I'm looking at |
|
102 the site as anonymous, e.g. not authenticated. If I login, I can now see: |
|
103 |
|
104 .. image:: ../../images/tutos-photowebsite_ui2.png |
|
105 |
|
106 Yeah, it's there! You will notice that I can see some entities as well as |
|
107 folders and images the anonymous user can't. It just works **everywhere in the |
|
108 ui** since it's handled at the repository level, thanks to our security model. |
|
109 |
|
110 Now if I click on the recently inserted folder, I can see |
|
111 |
|
112 .. image:: ../../images/tutos-photowebsite_ui3.png |
|
113 |
|
114 Great! There is even my pictures in the folder. I can know give to this folder a |
|
115 nicer name (provided I don't intend to import from it anymore, else already |
|
116 imported photos will be reimported), change permissions, title for some pictures, |
|
117 etc... Having a good content is much more difficult than having a good web site |
|
118 ;) |
|
119 |
|
120 |
|
121 Conclusion |
|
122 ~~~~~~~~~~ |
|
123 |
|
124 We started to see here an advanced feature of our repository: the ability |
|
125 to store some parts of our data-model into a custom storage, outside the |
|
126 database. There is currently only the :class:`BytesFileSystemStorage` available, |
|
127 but you can expect to see more coming in a near future (or write your own!). |
|
128 |
|
129 Also, we can know start to feed our web-site with some nice pictures! |
|
130 The site isn't perfect (far from it actually) but it's usable, and we can |
|
131 now start using it and improve it on the way. The Incremental Cubic Way :) |
|