Commits

Anonymous committed 220458e

Updated documentation. Version bump.

Comments (0)

Files changed (13)

+This file is probably outdated. Please use sphinx docs instead.
 
 Overview
 ========
+This file is probably outdated. Please use sphinx docs instead.
+
 ==Overview==
 
 django-photo-albums is a pluggable django image gallery app.

docs/_build/doctrees/environment.pickle

Binary file modified.

docs/_build/doctrees/index.doctree

Binary file modified.

docs/_build/html/_sources/index.txt

 and are available from different urls.
 
 Each image gallery provide functionality for image viewing, editing, 
-uploading, reordering, marking/unmarking as main and deleting.
+uploading, uploading entire albums in one zip file, reordering, 
+marking/unmarking as main and deleting.
 
 django-photo-albums requires Django >= 1.1 (or svn version with 
 url namespaces), setuptools for installation, django-generic-images 
-for image management and django-annoying. 
+for image management and django-annoying. django-generic-images and
+django-annoying will be installed automatically if you install 
+django-photo-albums via easy_install or pip.
 
-Sorl-thumbnails should be mentioned as a very good optional dependency for 
-generating thumbnails, but you can use any other library if you want.
+django-photo-albums does not provide any thumbnail creation solution because
+there are external django apps (such as sorl-thumbnail) that would do this 
+better. 
 
 Testing if app instance is integrated correctly (at least that templates 
 don't raise exceptions) is easy because base class for integration testcases 
 		
 	
 
-
 Templates used by django-photo-albums
 =====================================
 
 
 * ``<template_object_name>``: object for which gallery is attached to (the name 
     of variable is set in PhotoAlbumsSite constructor 
-    (:ref:`here<template_object_name>`), default is ``object``)	
+    (:ref:`here<template_object_name>`), default is ``'object'``)	
 	
-* ``current_app``: app name, 'albums' by default
+* ``current_app``: app name, ``'albums'`` by default
 
 
 Templates
 ---------
 
-The views included in django-photo-albums make use of these 8 templates:
+The views included in django-photo-albums make use of these 9 templates:
 
 
 * ``show_album.html`` displays entire album
 * ``upload_main_image.html`` - displays form for uploading one image. Uploaded 
 	image becomes main in gallery. Has ``form`` in context, it's a form of type 
 	:ref:`upload_form_class<upload_form_class>`.
+	
+* ``upload_zip.html`` - displays form for uploading zip archive with images.
+    Has ``form`` in context, it's a form of type 
+    :ref:`upload_zip_form_class<upload_zip_form_class>`
 
 * ``confirm_delete.html`` - displays confirmation dialog for deleting image. 
 	Has ``image`` in context. Should have a form that do POST request to delete 
 .. automodule:: photo_albums.views
 	:members:
 	:undoc-members:
+	
+Forms
+=====
 
+.. automodule:: photo_albums.forms
+
+    .. class:: photo_albums.forms.ImageEditForm()
+        
+        ::    
+
+            class ImageEditForm(forms.ModelForm):
+                class Meta:
+                    model = AttachedImage
+                    fields = ['caption']
+    
+    
+    .. _photoformset:
+    
+    .. class:: photo_albums.forms.PhotoFormSet
+    
+        ``modelformset_factory(AttachedImage, extra=3, fields = ['image', 'caption'])``
+
+    .. autoclass:: photo_albums.forms.UploadZipForm()
+        :members:
+        
+    .. autoclass:: photo_albums.forms.UploadZipAlbumForm
+        :members:
+        :show-inheritance:
+        
+    Example::
+    
+        if request.method == 'POST':
+            form = UploadZipAlbumForm(request.user, obj, request.POST, request.FILES)
+            if form.is_valid():
+                form.process_zip_file()
+                success_url = album_site.reverse('show_album', args=[object_id])
+                return HttpResponseRedirect(success_url)        
+        else:
+            form = UploadZipAlbumForm(request.user, obj)
+        
+            
+    
 
 *******
 Testing

docs/_build/html/genindex.html

 
    <h1 id="index">Index</h1>
 
-   <a href="#A"><strong>A</strong></a> | <a href="#C"><strong>C</strong></a> | <a href="#D"><strong>D</strong></a> | <a href="#E"><strong>E</strong></a> | <a href="#F"><strong>F</strong></a> | <a href="#I"><strong>I</strong></a> | <a href="#P"><strong>P</strong></a> | <a href="#S"><strong>S</strong></a> | <a href="#T"><strong>T</strong></a> | <a href="#U"><strong>U</strong></a> 
+   <a href="#A"><strong>A</strong></a> | <a href="#C"><strong>C</strong></a> | <a href="#D"><strong>D</strong></a> | <a href="#E"><strong>E</strong></a> | <a href="#F"><strong>F</strong></a> | <a href="#I"><strong>I</strong></a> | <a href="#N"><strong>N</strong></a> | <a href="#P"><strong>P</strong></a> | <a href="#S"><strong>S</strong></a> | <a href="#T"><strong>T</strong></a> | <a href="#U"><strong>U</strong></a> 
 
    <hr />
 
 <dl>
 
 <dt><a href="index.html#photo_albums.test_utils.AlbumTest.check">check() (photo_albums.test_utils.AlbumTest method)</a></dt>
-<dt><a href="index.html#photo_albums.views.clear_main_image">clear_main_image (in module photo_albums.views)</a></dt></dl></td><td width="33%" valign="top"><dl>
+<dt><a href="index.html#photo_albums.forms.UploadZipForm.clean_zip_file">clean_zip_file() (photo_albums.forms.UploadZipForm method)</a></dt></dl></td><td width="33%" valign="top"><dl>
+<dt><a href="index.html#photo_albums.views.clear_main_image">clear_main_image (in module photo_albums.views)</a></dt>
 </dl></td></tr></table>
 
 <h2 id="D">D</h2>
 <dl>
 
 <dt><a href="index.html#photo_albums.test_utils.AlbumTest.image2_in_album_id">image2_in_album_id (photo_albums.test_utils.AlbumTest attribute)</a></dt>
-<dt><a href="index.html#photo_albums.test_utils.AlbumTest.image_in_album_id">image_in_album_id (photo_albums.test_utils.AlbumTest attribute)</a></dt></dl></td><td width="33%" valign="top"><dl>
-<dt><a href="index.html#photo_albums.test_utils.AlbumTest.image_in_other_album_id">image_in_other_album_id (photo_albums.test_utils.AlbumTest attribute)</a></dt>
+<dt><a href="index.html#photo_albums.test_utils.AlbumTest.image_in_album_id">image_in_album_id (photo_albums.test_utils.AlbumTest attribute)</a></dt>
+<dt><a href="index.html#photo_albums.test_utils.AlbumTest.image_in_other_album_id">image_in_other_album_id (photo_albums.test_utils.AlbumTest attribute)</a></dt></dl></td><td width="33%" valign="top"><dl>
+<dt><a href="index.html#photo_albums.forms.UploadZipAlbumForm.is_valid_image">is_valid_image() (photo_albums.forms.UploadZipAlbumForm method)</a></dt>
+</dl></td></tr></table>
+
+<h2 id="N">N</h2>
+<table width="100%" class="indextable"><tr><td width="33%" valign="top">
+<dl>
+
+<dt><a href="index.html#photo_albums.forms.UploadZipAlbumForm.needs_unpacking">needs_unpacking() (photo_albums.forms.UploadZipAlbumForm method)</a></dt>
+  <dd><dl>
+    <dt><a href="index.html#photo_albums.forms.UploadZipForm.needs_unpacking">(photo_albums.forms.UploadZipForm method)</a></dt>
+  </dl></dd></dl></td><td width="33%" valign="top"><dl>
 </dl></td></tr></table>
 
 <h2 id="P">P</h2>
 <dl>
 
 <dt><a href="index.html#photo_albums.test_utils.AlbumTest.password">password (photo_albums.test_utils.AlbumTest attribute)</a></dt>
+<dt><a href="index.html#module-photo_albums.forms">photo_albums.forms (module)</a></dt>
+<dt><a href="index.html#photo_albums.forms.photo_albums.forms.ImageEditForm">photo_albums.forms.ImageEditForm (class in photo_albums.forms)</a></dt>
+<dt><a href="index.html#photo_albums.forms.photo_albums.forms.PhotoFormSet">photo_albums.forms.PhotoFormSet (class in photo_albums.forms)</a></dt>
 <dt><a href="index.html#module-photo_albums.test_utils">photo_albums.test_utils (module)</a></dt>
 <dt><a href="index.html#module-photo_albums.urls">photo_albums.urls (module)</a></dt></dl></td><td width="33%" valign="top"><dl>
 <dt><a href="index.html#module-photo_albums.views">photo_albums.views (module)</a></dt>
 <dt><a href="index.html#photo_albums.urls.PhotoAlbumSite">PhotoAlbumSite (class in photo_albums.urls)</a></dt>
+<dt><a href="index.html#photo_albums.forms.UploadZipAlbumForm.process_file">process_file() (photo_albums.forms.UploadZipAlbumForm method)</a></dt>
+  <dd><dl>
+    <dt><a href="index.html#photo_albums.forms.UploadZipForm.process_file">(photo_albums.forms.UploadZipForm method)</a></dt>
+  </dl></dd>
+<dt><a href="index.html#photo_albums.forms.UploadZipForm.process_zip_file">process_zip_file() (photo_albums.forms.UploadZipForm method)</a></dt>
 </dl></td></tr></table>
 
 <h2 id="S">S</h2>
 <dl>
 
 <dt><a href="index.html#photo_albums.views.upload_images">upload_images (in module photo_albums.views)</a></dt>
-<dt><a href="index.html#photo_albums.views.upload_main_image">upload_main_image (in module photo_albums.views)</a></dt></dl></td><td width="33%" valign="top"><dl>
+<dt><a href="index.html#photo_albums.views.upload_main_image">upload_main_image (in module photo_albums.views)</a></dt>
+<dt><a href="index.html#photo_albums.views.upload_zip">upload_zip (in module photo_albums.views)</a></dt>
+<dt><a href="index.html#photo_albums.forms.UploadZipAlbumForm">UploadZipAlbumForm (class in photo_albums.forms)</a></dt></dl></td><td width="33%" valign="top"><dl>
+<dt><a href="index.html#photo_albums.forms.UploadZipForm">UploadZipForm (class in photo_albums.forms)</a></dt>
 <dt><a href="index.html#photo_albums.test_utils.AlbumTest.username">username (photo_albums.test_utils.AlbumTest attribute)</a></dt>
 </dl></td></tr></table>
 

docs/_build/html/index.html

 templates, different permission rules, have dedicated integration test suites
 and are available from different urls.</p>
 <p>Each image gallery provide functionality for image viewing, editing,
-uploading, reordering, marking/unmarking as main and deleting.</p>
+uploading, uploading entire albums in one zip file, reordering,
+marking/unmarking as main and deleting.</p>
 <p>django-photo-albums requires Django &gt;= 1.1 (or svn version with
 url namespaces), setuptools for installation, django-generic-images
-for image management and django-annoying.</p>
-<p>Sorl-thumbnails should be mentioned as a very good optional dependency for
-generating thumbnails, but you can use any other library if you want.</p>
+for image management and django-annoying. django-generic-images and
+django-annoying will be installed automatically if you install
+django-photo-albums via easy_install or pip.</p>
+<p>django-photo-albums does not provide any thumbnail creation solution because
+there are external django apps (such as sorl-thumbnail) that would do this
+better.</p>
 <p>Testing if app instance is integrated correctly (at least that templates
 don&#8217;t raise exceptions) is easy because base class for integration testcases
 is provided.</p>
 
 {% url user_images:upload_images album_user.id %}
 
+{% url user_images:upload_zip album_user.id %}
+
 {% url user_images:show_image album_user.id image.id %}
 
 {% url user_images:edit_image album_user.id image.id %}
 <blockquote id="template-object-name">
 <p><tt class="docutils literal"><span class="pre">template_object_name</span></tt>: String. Optional. The name of template 
 context variable with object for which album is attached. 
-Default is &#8216;object&#8217;.</p>
+Default is <tt class="docutils literal"><span class="pre">'object'</span></tt>.</p>
 <p><tt class="docutils literal"><span class="pre">has_edit_permission</span></tt>: Optional. Function that accepts request and 
 object and returns True if user is allowed to edit album for 
 object and False otherwise. Default behaviour is to always 
 return True.</p>
 <p><tt class="docutils literal"><span class="pre">context_processors</span></tt>: Optional. A list of callables that will be 
 used as additional context_processors in each view.</p>
-<p id="edit-form-class"><tt class="docutils literal"><span class="pre">edit_form_class</span></tt>: Optional, default is <tt class="docutils literal"><span class="pre">ImageEditForm</span></tt> (defined 
-in <tt class="docutils literal"><span class="pre">photo_albums.forms</span></tt> module). ModelForm subclass to be used in 
-edit_image view.</p>
+<p id="edit-form-class"><tt class="docutils literal"><span class="pre">edit_form_class</span></tt>: Optional, default is 
+<tt class="xref docutils literal"><span class="pre">ImageEditForm</span></tt>. ModelForm subclass to be used in 
+<a title="photo_albums.views.edit_image" class="reference" href="#photo_albums.views.edit_image"><tt class="xref docutils literal"><span class="pre">edit_image()</span></tt></a> view.</p>
 <p id="upload-form-class"><tt class="docutils literal"><span class="pre">upload_form_class</span></tt>: Optional, default is <tt class="docutils literal"><span class="pre">AttachedImageForm</span></tt> (defined in 
 <tt class="docutils literal"><span class="pre">generic_images.forms</span></tt> module). ModelForm subclass to be used in 
-upload_main_image view.</p>
-<p id="upload-formset-class"><tt class="docutils literal"><span class="pre">upload_formset_class</span></tt>: Optional, default is <tt class="docutils literal"><span class="pre">PhotoFormSet</span></tt> (defined in 
-<tt class="docutils literal"><span class="pre">photo_albums.forms</span></tt> module). ModelFormSet to be used in upload_images view.</p>
+<a title="photo_albums.views.upload_main_image" class="reference" href="#photo_albums.views.upload_main_image"><tt class="xref docutils literal"><span class="pre">upload_main_image()</span></tt></a> view.</p>
+<p id="upload-formset-class"><tt class="docutils literal"><span class="pre">upload_formset_class</span></tt>: Optional, default is 
+<a class="reference" href="#photoformset"><em>PhotoFormSet</em></a>. ModelFormSet to be used in 
+<a title="photo_albums.views.upload_images" class="reference" href="#photo_albums.views.upload_images"><tt class="xref docutils literal"><span class="pre">upload_images()</span></tt></a> view.</p>
+<p id="upload-zip-form-class"><tt class="docutils literal"><span class="pre">upload_zip_form_class</span></tt>: Optional, default is 
+<a title="photo_albums.forms.UploadZipAlbumForm" class="reference" href="#photo_albums.forms.UploadZipAlbumForm"><tt class="xref docutils literal"><span class="pre">UploadZipAlbumForm</span></tt></a>. Form to be used in 
+<a title="photo_albums.views.upload_zip" class="reference" href="#photo_albums.views.upload_zip"><tt class="xref docutils literal"><span class="pre">upload_zip()</span></tt></a> view.</p>
 </blockquote>
 </dd></dl>
 
 <li><dl class="first docutils">
 <dt><tt class="docutils literal"><span class="pre">&lt;template_object_name&gt;</span></tt>: object for which gallery is attached to (the name</dt>
 <dd><p class="first last">of variable is set in PhotoAlbumsSite constructor
-(<a class="reference" href="#template-object-name"><em>here</em></a>), default is <tt class="docutils literal"><span class="pre">object</span></tt>)</p>
+(<a class="reference" href="#template-object-name"><em>here</em></a>), default is <tt class="docutils literal"><span class="pre">'object'</span></tt>)</p>
 </dd>
 </dl>
 </li>
-<li><p class="first"><tt class="docutils literal"><span class="pre">current_app</span></tt>: app name, &#8216;albums&#8217; by default</p>
+<li><p class="first"><tt class="docutils literal"><span class="pre">current_app</span></tt>: app name, <tt class="docutils literal"><span class="pre">'albums'</span></tt> by default</p>
 </li>
 </ul>
 </div>
 <div class="section" id="templates">
 <h4>Templates<a class="headerlink" href="#templates" title="Permalink to this headline">¶</a></h4>
-<p>The views included in django-photo-albums make use of these 8 templates:</p>
+<p>The views included in django-photo-albums make use of these 9 templates:</p>
 <ul>
 <li><p class="first"><tt class="docutils literal"><span class="pre">show_album.html</span></tt> displays entire album</p>
 </li>
 </dl>
 </li>
 <li><dl class="first docutils">
+<dt><tt class="docutils literal"><span class="pre">upload_zip.html</span></tt> - displays form for uploading zip archive with images.</dt>
+<dd><p class="first last">Has <tt class="docutils literal"><span class="pre">form</span></tt> in context, it&#8217;s a form of type
+<a class="reference" href="#upload-zip-form-class"><em>upload_zip_form_class</em></a></p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
 <dt><tt class="docutils literal"><span class="pre">confirm_delete.html</span></tt> - displays confirmation dialog for deleting image.</dt>
 <dd><p class="first last">Has <tt class="docutils literal"><span class="pre">image</span></tt> in context. Should have a form that do POST request to delete
 view on submit.</p>
 <tt class="descclassname">photo_albums.views.</tt><tt class="descname">upload_main_image</tt><a class="headerlink" href="#photo_albums.views.upload_main_image" title="Permalink to this definition">¶</a></dt>
 <dd>Upload 1 image and make it main image in gallery</dd></dl>
 
+<dl class="attribute">
+<dt id="photo_albums.views.upload_zip">
+<tt class="descclassname">photo_albums.views.</tt><tt class="descname">upload_zip</tt><a class="headerlink" href="#photo_albums.views.upload_zip" title="Permalink to this definition">¶</a></dt>
+<dd>Upload zip archive with images, extract them, check if they are correct 
+and attach to object. Redirect to <tt class="docutils literal"><span class="pre">show_album</span></tt> view on success.</dd></dl>
+
+</div>
+<div class="section" id="module-photo_albums.forms">
+<h3>Forms<a class="headerlink" href="#module-photo_albums.forms" title="Permalink to this headline">¶</a></h3>
+<dl class="class">
+<dt id="photo_albums.forms.photo_albums.forms.ImageEditForm">
+<em class="property">
+class </em><tt class="descclassname">photo_albums.forms.</tt><tt class="descname">ImageEditForm</tt><a class="headerlink" href="#photo_albums.forms.photo_albums.forms.ImageEditForm" title="Permalink to this definition">¶</a></dt>
+<dd><div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">ImageEditForm</span><span class="p">(</span><span class="n">forms</span><span class="o">.</span><span class="n">ModelForm</span><span class="p">):</span>
+    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
+        <span class="n">model</span> <span class="o">=</span> <span class="n">AttachedImage</span>
+        <span class="n">fields</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;caption&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<span class="target" id="photoformset"></span><dl class="class">
+<dt id="photo_albums.forms.photo_albums.forms.PhotoFormSet">
+<em class="property">
+class </em><tt class="descclassname">photo_albums.forms.</tt><tt class="descname">PhotoFormSet</tt><a class="headerlink" href="#photo_albums.forms.photo_albums.forms.PhotoFormSet" title="Permalink to this definition">¶</a></dt>
+<dd><tt class="docutils literal"><span class="pre">modelformset_factory(AttachedImage,</span> <span class="pre">extra=3,</span> <span class="pre">fields</span> <span class="pre">=</span> <span class="pre">['image',</span> <span class="pre">'caption'])</span></tt></dd></dl>
+
+<dl class="class">
+<dt id="photo_albums.forms.UploadZipForm">
+<em class="property">
+class </em><tt class="descclassname">photo_albums.forms.</tt><tt class="descname">UploadZipForm</tt><a class="headerlink" href="#photo_albums.forms.UploadZipForm" title="Permalink to this definition">¶</a></dt>
+<dd><p>A base form class for uploading several files packed as one .zip file.
+Extract files and provides hook for processing extracted files.
+During extraction it loads uncompressed files to memory by chunks so it 
+is safe to process zip archives with big files inside.</p>
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipForm.clean_zip_file">
+<tt class="descname">clean_zip_file</tt><big>(</big><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipForm.clean_zip_file" title="Permalink to this definition">¶</a></dt>
+<dd>Checks if zip file is not corrupted, stores in-memory uploaded file 
+to disk and returns path to stored file.</dd></dl>
+
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipForm.needs_unpacking">
+<tt class="descname">needs_unpacking</tt><big>(</big><em>name</em>, <em>info</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipForm.needs_unpacking" title="Permalink to this definition">¶</a></dt>
+<dd>Returns True is file should be extracted from zip and 
+False otherwise. Override in subclass to customize behaviour.
+Default is to unpack all files except directories and meta files 
+(names starts with &#8216;__&#8217;) .</dd></dl>
+
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipForm.process_file">
+<tt class="descname">process_file</tt><big>(</big><em>path</em>, <em>name</em>, <em>info</em>, <em>file_num</em>, <em>files_count</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipForm.process_file" title="Permalink to this definition">¶</a></dt>
+<dd><p>Override this in subclass to do something useful with files extracted 
+from uploaded zip archive.</p>
+<p>Params:</p>
+<ul class="simple">
+<li><tt class="docutils literal"><span class="pre">path</span></tt>: path to temporary file. It&#8217;s on developer to delete this file.</li>
+<li><tt class="docutils literal"><span class="pre">name</span></tt>: name of file in zip archive, returned by ZipFile.namelist()</li>
+<li><tt class="docutils literal"><span class="pre">info</span></tt>: file info, returned by ZipFile.infolist()</li>
+<li><tt class="docutils literal"><span class="pre">file_num</span></tt>: file&#8217;s order number</li>
+<li><tt class="docutils literal"><span class="pre">files_count</span></tt>: total files count</li>
+</ul>
+</dd></dl>
+
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipForm.process_zip_file">
+<tt class="descname">process_zip_file</tt><big>(</big><em>chunksize=65536</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipForm.process_zip_file" title="Permalink to this definition">¶</a></dt>
+<dd><p>Extract all files to temporary place and call process_file method 
+for each.</p>
+<p><tt class="docutils literal"><span class="pre">chunksize</span></tt> is the size of block in which compressed files are 
+read. Default is 64k. Do not set it below 64k because data from 
+compressed files will be read in blocks &gt;= 64k anyway.</p>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="class">
+<dt id="photo_albums.forms.UploadZipAlbumForm">
+<em class="property">
+class </em><tt class="descclassname">photo_albums.forms.</tt><tt class="descname">UploadZipAlbumForm</tt><big>(</big><em>user</em>, <em>obj</em>, <em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipAlbumForm" title="Permalink to this definition">¶</a></dt>
+<dd><p>Bases: <a title="photo_albums.forms.UploadZipForm" class="reference" href="#photo_albums.forms.UploadZipForm"><tt class="xref docutils literal"><span class="pre">photo_albums.forms.UploadZipForm</span></tt></a></p>
+<p>Form for uploading several images packed as one .zip file. 
+Only valid images are stored. Uploaded images are marked as uploaded
+by <tt class="docutils literal"><span class="pre">user</span></tt> and are attached to <tt class="docutils literal"><span class="pre">obj</span></tt> model.</p>
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipAlbumForm.is_valid_image">
+<tt class="descname">is_valid_image</tt><big>(</big><em>path</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipAlbumForm.is_valid_image" title="Permalink to this definition">¶</a></dt>
+<dd>Check if file is readable by PIL.</dd></dl>
+
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipAlbumForm.needs_unpacking">
+<tt class="descname">needs_unpacking</tt><big>(</big><em>name</em>, <em>info</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipAlbumForm.needs_unpacking" title="Permalink to this definition">¶</a></dt>
+<dd>Returns True is file should be extracted from zip and 
+False otherwise. Override in subclass to customize behaviour.
+Default is to skip directories, meta files 
+(names starts with <tt class="docutils literal"><span class="pre">'__'</span></tt>) and files with non-image extensions.</dd></dl>
+
+<dl class="method">
+<dt id="photo_albums.forms.UploadZipAlbumForm.process_file">
+<tt class="descname">process_file</tt><big>(</big><em>path</em>, <em>name</em>, <em>info</em>, <em>file_num</em>, <em>files_count</em><big>)</big><a class="headerlink" href="#photo_albums.forms.UploadZipAlbumForm.process_file" title="Permalink to this definition">¶</a></dt>
+<dd>Create AttachedImage instance if file is a valid image.</dd></dl>
+
+</dd></dl>
+
+<p>Example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+    <span class="n">form</span> <span class="o">=</span> <span class="n">UploadZipAlbumForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">POST</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">FILES</span><span class="p">)</span>
+    <span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">is_valid</span><span class="p">():</span>
+        <span class="n">form</span><span class="o">.</span><span class="n">process_zip_file</span><span class="p">()</span>
+        <span class="n">success_url</span> <span class="o">=</span> <span class="n">album_site</span><span class="o">.</span><span class="n">reverse</span><span class="p">(</span><span class="s">&#39;show_album&#39;</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="n">object_id</span><span class="p">])</span>
+        <span class="k">return</span> <span class="n">HttpResponseRedirect</span><span class="p">(</span><span class="n">success_url</span><span class="p">)</span>
+<span class="k">else</span><span class="p">:</span>
+    <span class="n">form</span> <span class="o">=</span> <span class="n">UploadZipAlbumForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span>
+</pre></div>
+</div>
 </div>
 </div>
 <div class="section" id="testing">
 </ul>
 </li>
 <li><a class="reference" href="#module-photo_albums.views">Views</a></li>
+<li><a class="reference" href="#module-photo_albums.forms">Forms</a></li>
 </ul>
 </li>
 <li><a class="reference" href="#testing">Testing</a><ul>

docs/_build/html/modindex.html

      <em></em></td></tr><tr class="cg-1">
      <td></td>
      <td>&nbsp;&nbsp;&nbsp;
+     <a href="index.html#module-photo_albums.forms"><tt class="xref">photo_albums.forms</tt></a></td><td>
+     <em></em></td></tr><tr class="cg-1">
+     <td></td>
+     <td>&nbsp;&nbsp;&nbsp;
      <a href="index.html#module-photo_albums.test_utils"><tt class="xref">photo_albums.test_utils</tt></a></td><td>
      <em></em></td></tr><tr class="cg-1">
      <td></td>

docs/_build/html/objects.inv

 photo_albums.views mod index.html
 photo_albums.urls mod index.html
 photo_albums.test_utils mod index.html
+photo_albums.forms mod index.html
 photo_albums.test_utils.AlbumTest.excluded_views attribute index.html
 photo_albums.test_utils.AlbumTest class index.html
 photo_albums.test_utils.AlbumTest.image2_in_album_id attribute index.html
 photo_albums.urls.PhotoAlbumSite class index.html
+photo_albums.forms.photo_albums.forms.ImageEditForm class index.html
+photo_albums.forms.UploadZipAlbumForm.process_file method index.html
+photo_albums.forms.UploadZipForm.clean_zip_file method index.html
+photo_albums.forms.UploadZipAlbumForm.needs_unpacking method index.html
+photo_albums.test_utils.AlbumTest.test_auth_views method index.html
+photo_albums.forms.UploadZipAlbumForm class index.html
 photo_albums.test_utils.AlbumTest.username attribute index.html
-photo_albums.test_utils.AlbumTest.test_auth_views method index.html
 photo_albums.test_utils.AlbumTest.image_in_album_id attribute index.html
 photo_albums.views.show_image function index.html
 photo_albums.views.edit_image attribute index.html
 photo_albums.views.delete_image attribute index.html
 photo_albums.test_utils.AlbumTest.test_public_views method index.html
 photo_albums.views.show_album function index.html
+photo_albums.forms.UploadZipForm.process_zip_file method index.html
+photo_albums.forms.UploadZipAlbumForm.is_valid_image method index.html
 photo_albums.test_utils.AlbumTest.album_for_id attribute index.html
+photo_albums.forms.UploadZipForm class index.html
 photo_albums.views.upload_main_image attribute index.html
 photo_albums.views.set_as_main_image attribute index.html
+photo_albums.forms.UploadZipForm.process_file method index.html
 photo_albums.test_utils.AlbumTest.password attribute index.html
 photo_albums.test_utils.AlbumTest.test_forbidden_views method index.html
 photo_albums.test_utils.AlbumTest.image_in_other_album_id attribute index.html
 photo_albums.views.edit_album attribute index.html
 photo_albums.views.upload_images attribute index.html
+photo_albums.forms.UploadZipForm.needs_unpacking method index.html
 photo_albums.views.clear_main_image attribute index.html
 photo_albums.test_utils.AlbumTest.check method index.html
+photo_albums.forms.photo_albums.forms.PhotoFormSet class index.html
+photo_albums.views.upload_zip attribute index.html

docs/_build/html/searchindex.js

-Search.setIndex({desctypes:{"0":"attribute","1":"class","2":"method","3":"function"},terms:{pluggabl:0,has_edit_permiss:0,obiwanu:0,formset:0,all:0,prefix:[],subclass:0,follow:0,alt:0,photo_album:0,depend:0,string:0,fals:0,account:0,util:[],context_processor:0,veri:0,contenttyp:0,ref:[],list:0,upload:0,iter:0,pleas:0,unmark:0,accounts_photo_sit:0,second:0,pass:0,index:[],appear:0,ned:0,delet:0,version:0,method:0,gener:0,here:0,errorlist:[],upload_form_class:0,becom:0,valu:0,search:[],error_class:[],vasia:0,"0x1a1bb30":[],app_util:[],chang:0,extra:0,modul:0,href:0,instal:0,from:0,current_app:0,next:0,prev:0,type:0,reorder:0,peopl:0,relat:0,enhanc:0,accept:0,sorl:0,obj:0,must:0,none:0,edit_imag:0,alia:[],setup:0,work:0,uniqu:0,annoi:0,can:0,purpos:0,overrid:0,edit_form_class:0,want:0,view_us:[],templat:0,tag:0,smth:0,login_requir:[],alwai:0,multipl:0,attachedimageform:0,"__init__":[],instead:0,photoformset:0,clone:0,mai:0,data:0,"0x1a19b70":[],django:0,issu:0,object_regex:0,allow:0,media:[],callabl:0,fallback:0,order:0,becaus:0,clear_main_imag:0,"0x1a1caf0":[],paramet:0,write:0,thank:0,img:0,html:0,main:0,edit_album:0,good:0,"return":0,python:0,auth:0,mention:0,name:0,edit:0,photoalbumssit:0,revers:0,each:0,attachedimag:[],show_imag:0,runtest:0,meta:[],my_fixtur:0,album:0,upload_imag:0,test_auth_view:0,content:[],modelform:0,integr:0,urlpattern:0,empty_permit:[],base:0,org:0,caption:0,place:0,urlconf:0,lambda:0,instance_nam:0,template_nam:0,onc:0,alreadi:0,installed_app:0,auto_id:[],differ:0,account_imag:[],least:0,tou:0,attach:0,"0x1a1aaf0":[],test_forbidden_view:0,scheme:[],option:0,namespac:0,setuptool:0,kind:0,provid:0,initi:[],"0x1a14af0":[],queryset:0,ani:0,have:0,tabl:0,need:0,dedic:0,exclud:0,object_id:0,note:0,indic:[],necesseri:0,album_sit:0,endfor:0,usernam:0,object:0,galleri:0,label_suffix:[],regular:0,deploi:0,lookup_field:0,"class":0,don:0,url:0,request:0,"100x50":0,enctyp:0,show:0,get_template_nam:[],model_nam:0,permiss:0,redirect:0,should:0,dict:0,folder:0,extra_context:0,variou:0,delete_imag:0,express:0,set_as_main_imag:0,report:0,requir:0,useralbumtest:0,attachedimageformformset:[],stuff:0,common:0,contain:0,where:0,view:0,set:0,methodnam:0,see:[],multipart:0,testcas:0,modelformset:0,statu:0,correctli:0,pattern:0,user:0,thumb:[],won:0,"import":0,thumbnail:0,extend:[],image_id:0,entir:0,addit:0,behaviour:0,etc:0,excluded_view:0,context:0,load:0,upload_formset_class:0,reorder_imag:0,assum:0,mark:0,basic:0,imag:0,bulk:0,look:0,defin:0,fixtur:0,endblock:[],them:0,kwarg:0,sever:0,decor:[],welcom:0,perform:0,suggest:0,make:0,same:[],instanc:0,document:0,complet:0,http:0,action:0,rais:0,album_us:0,userprofile__slug:0,test_help:0,upload_main_imag:0,exampl:0,thi:0,model:0,usual:0,photo:0,test_public_view:0,test_util:0,thees:[],easi:0,except:0,add:0,input:0,save:0,app:0,photoalbumsit:0,which:0,password:0,template_object_nam:0,like:0,"0x1a1baf0":[],albumtest:0,album_for_id:0,necessari:0,page:[],test_reord:0,syncdb:0,librari:0,confirm:0,per:0,tracker:0,pluggablesit:[],imageeditform:0,id_:[],previou:0,run:0,usag:0,step:0,post:0,viewtest:0,slug:0,plug:0,src:0,kmike:0,manag:0,user_imag:0,dialog:0,constructor:0,image_in_other_album_id:0,block:[],image2_in_album_id:0,photo_smal:[],set_image_ord:0,easy_instal:0,your:0,submit:0,custom:[],avail:0,show_album:0,includ:0,suit:0,"function":0,form:0,"0x1a19bb0":[],link:0,"true":0,bug:0,possibl:0,"default":0,autoclass:[],displai:0,site:0,otherwis:0,confirm_delet:0,app_nam:0,featur:0,creat:0,"0x1a1bb70":[],exist:0,file:[],pip:0,check:0,"_custom_url_schem":[],field:0,other:0,lookup:0,test:0,you:0,variabl:0,image_in_album_id:0,get_profil:[],svn:0,my_object_slug:0,bitbucket:0,rule:0,generic_imag:0,generic_util:0},titles:["django-photo-albums&#8217;s documentation"],modules:{"photo_albums.views":0,"photo_albums.urls":0,"photo_albums.test_utils":0},descrefs:{"photo_albums.views":{upload_images:[0,0],edit_album:[0,0],set_as_main_image:[0,0],edit_image:[0,0],upload_main_image:[0,0],delete_image:[0,0],show_album:[0,3],show_image:[0,3],clear_main_image:[0,0]},"photo_albums.test_utils.AlbumTest":{username:[0,0],album_for_id:[0,0],image_in_other_album_id:[0,0],excluded_views:[0,0],test_auth_views:[0,2],image_in_album_id:[0,0],album_site:[0,0],test_public_views:[0,2],test_reorder:[0,2],test_forbidden_views:[0,2],fixtures:[0,0],password:[0,0],check:[0,2],image2_in_album_id:[0,0]},"photo_albums.urls":{PhotoAlbumSite:[0,1]},"photo_albums.test_utils":{AlbumTest:[0,1]}},filenames:["index"]})
+Search.setIndex({desctypes:{"0":"attribute","1":"class","2":"method","3":"function"},terms:{pluggabl:0,has_edit_permiss:0,obiwanu:0,skip:0,formset:0,all:0,prefix:[],"0x1a19b70":[],follow:0,alt:0,disk:0,photo_album:0,depend:[],readabl:0,string:0,fals:0,account:0,util:[],context_processor:0,upload_zip_form_class:0,veri:[],contenttyp:0,ref:[],list:0,upload:0,iter:0,pleas:0,unmark:0,accounts_photo_sit:0,id_:[],second:0,pass:0,index:[],appear:0,ned:0,delet:0,version:0,method:0,gener:0,here:0,errorlist:[],upload_form_class:0,path:0,becom:0,valu:0,search:[],error_class:[],vasia:0,"0x1a1bb30":[],action:0,chang:0,via:0,extra:0,modul:0,href:0,instal:0,should:0,regex:[],from:0,zip:0,memori:0,current_app:0,next:0,call:0,prev:0,type:0,reorder:0,src:0,peopl:0,relat:0,enhanc:0,templat:0,sorl:0,obj:0,unpack:0,must:0,none:0,edit_imag:0,alia:[],setup:0,work:0,uniqu:0,annoi:0,archiv:0,can:0,existingfil:[],purpos:0,overrid:0,edit_form_class:0,want:0,process:0,view_us:[],indic:[],tag:0,smth:0,login_requir:[],alwai:0,multipl:0,write:0,"__init__":[],instead:0,photoformset:0,clone:0,mai:0,infolist:0,data:0,django:0,issu:0,object_regex:0,allow:0,media:[],callabl:0,fallback:0,order:0,becaus:0,clear_main_imag:0,"0x1a1caf0":[],paramet:0,process_zip_fil:0,thank:0,img:0,better:0,html:0,main:0,edit_album:0,good:[],"return":0,thei:0,python:0,safe:0,auth:0,mention:[],uploadzipform:0,name:0,edit:0,photoalbumssit:0,revers:0,each:0,attachedimag:0,show_imag:0,chunk:0,process_fil:0,runtest:0,meta:0,my_fixtur:0,extract:0,album:0,upload_imag:0,test_auth_view:0,content:[],modelform:0,correct:0,integr:0,namelist:0,urlpattern:0,insid:0,empty_permit:[],base:0,org:0,caption:0,place:0,temporari:0,urlconf:0,lambda:0,instance_nam:0,template_nam:0,onc:0,number:0,hook:0,alreadi:0,installed_app:0,size:0,auto_id:[],differ:0,start:0,account_imag:[],least:0,tou:0,attach:0,"0x1a1aaf0":[],accept:0,test_forbidden_view:0,scheme:[],zipfil:0,store:0,modelformset_factori:0,option:0,namespac:0,setuptool:0,kind:0,provid:0,success_url:0,initi:[],"0x1a14af0":[],queryset:0,ani:0,corrupt:0,have:0,tabl:0,need:0,dedic:0,exclud:0,object_id:0,note:0,which:0,necesseri:0,album_sit:0,endfor:0,usernam:0,object:0,compress:0,galleri:0,label_suffix:[],regular:0,deploi:0,lookup_field:0,"class":0,don:0,url:0,request:0,doe:0,"100x50":0,enctyp:0,"64k":0,show:0,get_template_nam:[],model_nam:0,permiss:0,anywai:0,filefield:[],redirect:0,onli:0,solut:0,temporary_file_path:[],dict:0,folder:0,extra_context:0,count:0,variou:0,delete_imag:0,express:0,set_as_main_imag:0,report:0,requir:0,useralbumtest:0,attachedimageformformset:[],is_valid_imag:0,stuff:0,common:0,contain:0,where:0,valid:0,view:0,user:0,set:0,methodnam:0,see:[],multipart:0,arg:0,testcas:0,close:[],modelformset:0,uploadzipalbumform:0,statu:0,correctli:0,pattern:0,someth:0,thumb:[],clean_zip_fil:0,won:0,"import":0,thumbnail:0,extend:[],image_id:0,entir:0,addit:0,behaviour:0,extens:0,etc:0,excluded_view:0,context:0,load:0,inspir:[],upload_formset_class:0,reorder_imag:0,non:0,assum:0,files_count:0,mark:0,basic:0,imag:0,bulk:0,els:0,look:0,defin:0,fixtur:0,endblock:[],pack:0,them:0,kwarg:0,sever:0,decor:[],develop:0,welcom:0,perform:0,suggest:0,make:0,same:[],instanc:0,document:0,complet:0,http:0,app_util:[],rais:0,album_us:0,userprofile__slug:0,extern:0,test_help:0,upload_main_imag:0,exampl:0,thi:0,model:0,file_num:0,usual:0,photo:0,test_public_view:0,test_util:0,thees:[],easi:0,except:0,param:0,add:0,is_valid:0,input:0,save:0,app:0,photoalbumsit:0,read:0,big:0,password:0,template_object_nam:0,like:0,success:0,"0x1a1baf0":[],albumtest:0,album_for_id:0,necessari:0,photologu:[],page:[],test_reord:0,creation:0,syncdb:0,librari:[],total:0,confirm:0,chunksiz:0,subclass:0,tracker:0,pluggablesit:[],imageeditform:0,httpresponseredirect:0,previou:0,run:0,uncompress:0,usag:0,step:0,post:0,viewtest:0,slug:0,plug:0,upload_zip:0,kmike:0,would:0,manag:0,user_imag:0,dialog:0,constructor:0,image_in_other_album_id:0,block:0,image2_in_album_id:0,photo_smal:[],set_image_ord:0,easy_instal:0,automat:0,your:0,per:0,submit:0,custom:0,avail:0,show_album:0,attachedimageform:0,includ:0,suit:0,"function":0,form:0,"0x1a19bb0":[],link:0,"true":0,bug:0,info:0,needs_unpack:0,possibl:0,"default":0,autoclass:[],displai:0,below:0,site:0,otherwis:0,confirm_delet:0,app_nam:0,featur:0,pil:0,creat:0,"0x1a1bb70":[],dure:0,exist:0,file:0,pip:0,check:0,"_custom_url_schem":[],field:0,other:0,lookup:0,test:0,you:0,variabl:0,image_in_album_id:0,get_profil:[],svn:0,my_object_slug:0,bitbucket:0,directori:0,rule:0,generic_imag:0,generic_util:0},titles:["django-photo-albums&#8217;s documentation"],modules:{"photo_albums.views":0,"photo_albums.urls":0,"photo_albums.forms":0,"photo_albums.test_utils":0},descrefs:{"photo_albums.test_utils.AlbumTest":{username:[0,0],album_for_id:[0,0],image_in_other_album_id:[0,0],excluded_views:[0,0],test_auth_views:[0,2],image_in_album_id:[0,0],album_site:[0,0],test_public_views:[0,2],test_reorder:[0,2],test_forbidden_views:[0,2],fixtures:[0,0],password:[0,0],check:[0,2],image2_in_album_id:[0,0]},"photo_albums.urls":{PhotoAlbumSite:[0,1]},"photo_albums.forms.UploadZipForm":{process_zip_file:[0,2],clean_zip_file:[0,2],process_file:[0,2],needs_unpacking:[0,2]},"photo_albums.forms":{UploadZipAlbumForm:[0,1],UploadZipForm:[0,1]},"photo_albums.views":{upload_images:[0,0],edit_album:[0,0],set_as_main_image:[0,0],upload_zip:[0,0],edit_image:[0,0],upload_main_image:[0,0],delete_image:[0,0],show_album:[0,3],show_image:[0,3],clear_main_image:[0,0]},"photo_albums.test_utils":{AlbumTest:[0,1]},"photo_albums.forms.photo_albums.forms":{PhotoFormSet:[0,1],ImageEditForm:[0,1]},"photo_albums.forms.UploadZipAlbumForm":{is_valid_image:[0,2],process_file:[0,2],needs_unpacking:[0,2]}},filenames:["index"]})
 # built documents.
 #
 # The short X.Y version.
-version = '0.16'
+version = '0.20'
 # The full version, including alpha/beta/rc tags.
-release = '0.16'
+release = '0.20'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 and are available from different urls.
 
 Each image gallery provide functionality for image viewing, editing, 
-uploading, reordering, marking/unmarking as main and deleting.
+uploading, uploading entire albums in one zip file, reordering, 
+marking/unmarking as main and deleting.
 
 django-photo-albums requires Django >= 1.1 (or svn version with 
 url namespaces), setuptools for installation, django-generic-images 
-for image management and django-annoying. 
+for image management and django-annoying. django-generic-images and
+django-annoying will be installed automatically if you install 
+django-photo-albums via easy_install or pip.
 
-Sorl-thumbnails should be mentioned as a very good optional dependency for 
-generating thumbnails, but you can use any other library if you want.
+django-photo-albums does not provide any thumbnail creation solution because
+there are external django apps (such as sorl-thumbnail) that would do this 
+better. 
 
 Testing if app instance is integrated correctly (at least that templates 
 don't raise exceptions) is easy because base class for integration testcases 
 		
 	
 
-
 Templates used by django-photo-albums
 =====================================
 
 
 * ``<template_object_name>``: object for which gallery is attached to (the name 
     of variable is set in PhotoAlbumsSite constructor 
-    (:ref:`here<template_object_name>`), default is ``object``)	
+    (:ref:`here<template_object_name>`), default is ``'object'``)	
 	
-* ``current_app``: app name, 'albums' by default
+* ``current_app``: app name, ``'albums'`` by default
 
 
 Templates
 ---------
 
-The views included in django-photo-albums make use of these 8 templates:
+The views included in django-photo-albums make use of these 9 templates:
 
 
 * ``show_album.html`` displays entire album
 * ``upload_main_image.html`` - displays form for uploading one image. Uploaded 
 	image becomes main in gallery. Has ``form`` in context, it's a form of type 
 	:ref:`upload_form_class<upload_form_class>`.
+	
+* ``upload_zip.html`` - displays form for uploading zip archive with images.
+    Has ``form`` in context, it's a form of type 
+    :ref:`upload_zip_form_class<upload_zip_form_class>`
 
 * ``confirm_delete.html`` - displays confirmation dialog for deleting image. 
 	Has ``image`` in context. Should have a form that do POST request to delete 
 .. automodule:: photo_albums.views
 	:members:
 	:undoc-members:
+	
+Forms
+=====
 
+.. automodule:: photo_albums.forms
+
+    .. class:: photo_albums.forms.ImageEditForm()
+        
+        ::    
+
+            class ImageEditForm(forms.ModelForm):
+                class Meta:
+                    model = AttachedImage
+                    fields = ['caption']
+    
+    
+    .. _photoformset:
+    
+    .. class:: photo_albums.forms.PhotoFormSet
+    
+        ``modelformset_factory(AttachedImage, extra=3, fields = ['image', 'caption'])``
+
+    .. autoclass:: photo_albums.forms.UploadZipForm()
+        :members:
+        
+    .. autoclass:: photo_albums.forms.UploadZipAlbumForm
+        :members:
+        :show-inheritance:
+        
+    Example::
+    
+        if request.method == 'POST':
+            form = UploadZipAlbumForm(request.user, obj, request.POST, request.FILES)
+            if form.is_valid():
+                form.process_zip_file()
+                success_url = album_site.reverse('show_album', args=[object_id])
+                return HttpResponseRedirect(success_url)        
+        else:
+            form = UploadZipAlbumForm(request.user, obj)
+        
+            
+    
 
 *******
 Testing
 
 setup(
       name='django-photo-albums',
-      version='0.16',
+      version='0.20',
       author='Mikhail Korobov',
       author_email='kmike84@gmail.com',
       url='http://bitbucket.org/kmike/django-photo-albums/',      
       
       description = 'Pluggable Django image gallery app.',
       license = 'MIT license',
-      packages=['photo_albums'],
+      packages=['photo_albums', 'photo_albums.lib'],
+      package_data={'photo_albums': ['locale/en/LC_MESSAGES/*','locale/ru/LC_MESSAGES/*']},      
+      
       requires = ['django (>=1.1)'],
-      install_requires=['django-generic-images >= 0.29', 'django-annoying > 0.7'],
+      install_requires=['django-generic-images >= 0.30', 'django-annoying > 0.7'],
       
-      classifiers=(
+      classifiers=[
           'Development Status :: 3 - Alpha',
-          'Environment :: Plugins',
+          'Environment :: Web Environment',
           'Framework :: Django',
           'Intended Audience :: Developers',
           'License :: OSI Approved :: MIT License',
-          'Natural Language :: English',
-          'Natural Language :: Russian',
           'Programming Language :: Python',
           'Topic :: Software Development :: Libraries :: Python Modules'
-        ),
+        ],
 )