Commits

Roman Tolkachyov  committed 3d0d7dd

С этого момента всё на русском

  • Participants
  • Parent commits 83fa95e

Comments (0)

Files changed (52)

File .bash_profile

+alias mkdoc="sphinx-build -b html ./doc ./html_doc"

File doc/Makefile

+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/easycouch.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/easycouch.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/easycouch"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/easycouch"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+# -*- coding: utf-8 -*-
+#
+# easycouch documentation build configuration file, created by
+# sphinx-quickstart on Thu Feb 23 19:34:33 2012.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.insert(0, os.path.abspath('..'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'easycouch'
+copyright = u'2012, Roman Tolkachyov'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.3dev'
+# The full version, including alpha/beta/rc tags.
+release = '0.3dev'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'easycouchdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'easycouch.tex', u'easycouch Documentation',
+   u'Roman Tolkachyov', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'easycouch', u'easycouch Documentation',
+     [u'Roman Tolkachyov'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'easycouch', u'easycouch Documentation',
+   u'Roman Tolkachyov', 'easycouch', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'

File doc/index.rst

+.. easycouch documentation master file, created by
+   sphinx-quickstart on Thu Feb 23 19:34:33 2012.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+EasyCouch – документация
+========================
+
+Оглавление:
+
+.. toctree::
+   :maxdepth: 2
+
+Основы работы
+-------------
+.. <
+
+Рассмотрим пример работы с библиотекой
+
+>>> couch = EasyCouch("http://localhost:5984/", {'default': 'default', 'comments': 'real_comments'})
+
+Для начала следует создать объект EasyCouch. В дальнейшем все действия с сервером производятся через этот объект. Первым параметром передается адрес сервера, вторым – карта имен баз данных. Здесь мы можем указать синонимы для баз
+данных которые мы хотим использовать. Ключу соответствует синоним, а значению – реальное имя БД. Теперь можно получить
+объект Database, позволяющий работать с отдельной БД:
+
+>>> db = couch.comments
+
+Так же присутствует возможность получить доступ к любой БД по её реальному имени
+
+>>> db = couch.get_db('real_comments')
+
+Библиотека EasyCouch устроена таким образом, что позволяет хранить в CouchDB объекты любого типа. Например, наш класс:
+
+.. sourcecode:: python
+	
+	class BlogPost(object):
+		pass
+
+Прежде чем сохранить первый объект в базе, необходимо создать и зарегистрировать ``Mapper`` для данного типа объектов
+
+>>> mapper = Mapper(BlogPost)
+>>> db.register_mapper("blogpost", mapper)
+
+``Mapper`` – это объект, знающий КАК преобразовать python-объект в структуру, пригодную для хранения в CouchDB, и обратно.
+Первым аргументом в ``__init__`` передается ``doctype`` – это имя будет сохраняться вместе с объектом в БД для нужд
+последующего преобразования JSON структуры couchdb в python-объект. Вторым аргументом передается сам класс, для которого данный мапер актуален.
+
+Далее объект создается самым тривиальным путём и отправляется в базу данных для сохранения:
+
+>>> my_post = BlogPost()
+>>> my_post._id = "test_post"
+>>> my_post.title = "hello easycouch"
+>>> db.store(my_post)
+
+В случае если у объекта отсутствует свойство ``_id``, ``store`` сгенерирует его стандартным алгоритмом
+
+>>> my_post = db.get("test_post")
+>>> my_post.title
+u"hello easycouch"
+
+.. >
+
+Дизайн-документы
+----------------
+.. <
+
+Виды (view) – главный инструмент запросов к CouchDB. В простейшем случае, вид можно создать так:
+
+>>> my_view = View("function(doc) { emit(doc.title); }")
+>>> resoults = my_view(db, limit = 10, include_docs = True)
+
+В примере выше ``my_view`` не привязан к дизайн документу, поэтому при его вызове необходимо
+первым аргументом передать экземпляр ``Database`` (остальные аргументы – параметры запроса).
+Привязать вид к дизайн-документу можно разными способами. Например:
+
+>>> my_design = DesignDocument()
+>>> my_design.add_view('some_view', my_view)
+
+или:
+
+>>> class MyDesign(DesignDocument):
+>>> 	some_view = View("function(doc) { emit(doc.title); }")
+>>> my_design = MyDesign()
+
+Вызывать виды можно по соответствующему имени
+
+>>> type(my_design.some_view)
+<class 'easycouch.view.View'>
+
+Поскольку дизайн-документ должен являться частью базы данных, его следует там зарегистрировать:
+
+>>> db.register_design('my_design', my_design)
+
+и тогда наш вид можно вызывать без первого аргумента `db`
+
+>>> resoults = db.my_design.some_view(limit = 10, include_docs = True)
+
+В случае если дизайн-документ синхронизирован с базой данных, easycouch выполнит запрос к этому
+виду, в ином случае вид будет запущен как временный (temp view).
+
+>>> couch.sync()
+>>> db.hello_design.some_view() # HTTP /db/_design/my_design/_view/some_view
+
+Проще всего организовывать код дизайн-документов в директорию вида
+
+- designs
+	- my_design
+		- some_view
+			- map.js
+			- reduce.js
+
+Тогда код самих функций для всего дизайн-документа можно подгрузить используя
+``sources_from_path``. В таком случае определение видов в ``DesignDocument`` можно использовать 
+только для задания параметров "по-умолчанию":
+
+.. sourcecode:: python
+	
+	class MyDesign(DesignDocument):
+		some_view = View(limit = 10, include_docs = True)
+	my_design.sources_from_path('./design/my_design')
+.. >
+
+
+Работа с результатами
+---------------------
+.. <
+
+``ViewResoults`` позволяет работать с результатами вида разными способами. Поскольку сам по себе
+этот объект является итератором, можно пройтись по результатам следующим образом:
+
+.. sourcecode:: python
+	
+	for key, object_or_value in resoults:
+		pass
+
+Первым значением в этом случае всегда будет являться ключ, а второй параметр может быть 
+объектом, если ``include_docs = True``, либо значением (которое преобразуется в объект если
+задан doctype и для него есть mapper) в ином случае. Так же можно использовать ``.items()``,
+``.objects()``, ``.values()``.
+.. >
+
+API
+===
+
+.. autoclass:: easycouch.EasyCouch
+	:members:
+
+.. autoclass:: easycouch.Database
+	:members:
+
+.. autoclass:: easycouch.DesignDocument
+	:members:
+
+.. autoclass:: easycouch.View
+	:members:
+
+.. autoclass:: easycouch.Mapper
+	:members:
+
+.. automodule:: easycouch.properties
+	:members:
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+

File doc/make.bat

+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html       to make standalone HTML files
+	echo.  dirhtml    to make HTML files named index.html in directories
+	echo.  singlehtml to make a single large HTML file
+	echo.  pickle     to make pickle files
+	echo.  json       to make JSON files
+	echo.  htmlhelp   to make HTML files and a HTML help project
+	echo.  qthelp     to make HTML files and a qthelp project
+	echo.  devhelp    to make HTML files and a Devhelp project
+	echo.  epub       to make an epub
+	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  text       to make text files
+	echo.  man        to make manual pages
+	echo.  texinfo    to make Texinfo files
+	echo.  gettext    to make PO message catalogs
+	echo.  changes    to make an overview over all changed/added/deprecated items
+	echo.  linkcheck  to check all external links for integrity
+	echo.  doctest    to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "singlehtml" (
+	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\easycouch.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\easycouch.ghc
+	goto end
+)
+
+if "%1" == "devhelp" (
+	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished.
+	goto end
+)
+
+if "%1" == "epub" (
+	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The epub file is in %BUILDDIR%/epub.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "text" (
+	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The text files are in %BUILDDIR%/text.
+	goto end
+)
+
+if "%1" == "man" (
+	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The manual pages are in %BUILDDIR%/man.
+	goto end
+)
+
+if "%1" == "texinfo" (
+	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+	goto end
+)
+
+if "%1" == "gettext" (
+	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end

File easycouch/__init__.py

-from document import Document, InlineDocument
-from couch import EasyCouch, viewdef
-from view import ViewDefenition
+from couch import EasyCouch
+from database import Database
+from design import DesignDocument
+from view import View, ViewResoults
+from mapper import Mapper

File easycouch/couch.py

 #coding=utf-8
 import logging
-import uuid
-from couchdb import Server, ResourceNotFound
-from properties import BaseProperty
-from document import Document, InlineDocument
-from view import ViewDefenition, EasyRow
-from utils import LazyDocument
-
-class DocumentMapper(object):
-	def __init__(self, couch, cls_ref):
-		self.couch = couch
-		self.cls_ref = cls_ref
-		self.properties = {}
-		for prop_name in dir(cls_ref):
-			prop = getattr(cls_ref, prop_name)
-			if isinstance(prop, BaseProperty):
-				self.add_property(prop_name, prop)
-	
-	def to_obj(self, json_data):
-		obj = self.cls_ref()
-		for key, value in json_data.items():
-			if self.has_property(key):
-				value = self.get_property(key).from_db(value)
-			else:
-				value = self.struct_to_obj(value)
-			setattr(obj, key, value)
-		return obj
-	
-	def struct_to_obj(self, value):
-		""" Преобразовать структуру (dict, list) couch в структуру объекта
-		Так же выявляет в структурах inline-документы и ссылки.
-		"""
-		if isinstance(value, dict) and value.has_key('doctype'):
-			doctype = value['doctype']
-			value = self.couch.get_mapper(doctype).to_obj(value)
-		elif isinstance(value, dict) and value.has_key('_id'):
-			print "make lazy", value
-			value = LazyDocument(self.couch, value['_id'])
-		elif isinstance(value, dict):
-			for key in value:
-				value[key] = self.struct_to_obj(value[key])
-		elif isinstance(value, list):
-			i = 0
-			for item in value:
-				value[i] = self.struct_to_obj(item)
-				i+=1
-		return value
-	
-	def struct_to_json(self, value):
-		""" Преобразует структуру из объектов python в простую json структуру"""
-		if isinstance(value, Document):
-			value = {'_id': value._id}
-		elif isinstance(value, LazyDocument):
-			value = {'_id': value.obj_id}
-			print "found lazy"
-		elif isinstance(value, InlineDocument):
-			value = self.couch.get_mapper(value.doctype or value.__class__.__name__).to_json(value)
-		elif isinstance(value, list):
-			i = 0
-			for item in value:
-				value[i] = self.struct_to_json(item)
-				i+=1
-		elif isinstance(value, dict):
-			for key in value:
-				value[key] = self.struct_to_json(value[key])
-		return value
-	
-	def to_json(self, obj):
-		obj_data = obj._get_state_diff()
-		resoult = {}
-		for name, prop in self.properties.items():
-			if obj_data.has_key(name):
-				resoult[name] = prop.to_db(obj_data[name])
-			elif hasattr(obj, name):
-				resoult[name] = prop.to_db(getattr(obj, name))
-			else:
-				resoult[name] = prop.to_db(prop.get_default())
-		for key,value in obj_data.items():
-			if not self.has_property(key):
-				resoult[key] = self.struct_to_json(value)
-		return resoult
-	
-	def add_property(self, name, prop):
-		self.properties[name] = prop
-	
-	def has_property(self, name):
-		return self.properties.has_key(name)
-		
-	def get_property(self, name):
-		return self.properties[name]
+from .http import ServerHttpApi
+from .database import Database
 
 class EasyCouch(object):
-	view_defs = {}
-	
-	def __init__(self, db_name = None, *args, **kwargs):
-		self.server = Server(*args, **kwargs)
-		self.registered_doctypes = {}
-		if not db_name is None:
-			self.select_db(db_name)
-		self.register_doc(Document)
-	
-	def select_db(self, db_name):
-		""" Выбрать базу данных
-		Выбирает базу данных как текущую. Создает если не существует. Последующие запросы
-		будут адресованы этой базе данных."""
-		try:
-			self.db = self.server[db_name]
-		except ResourceNotFound:
-			self.db = self.server.create(db_name)
-			logging.info("New db %s was created" % db_name, 1)
-		self.db_name = db_name
-	
-	def register_doc(self, cls_ref, doctype = None):
-		""" Регистрация документа для маппинга. В случае если doctype не указан, используется
-		имя переданного класса."""
-		if doctype is None:
-			doctype = cls_ref.__name__
-		if self.registered_doctypes.has_key(doctype):
-			raise Exception("Doctype '%s' already registered" % doctype)
-		self.registered_doctypes[doctype] = DocumentMapper(self, cls_ref)
-		for prop_name in dir(cls_ref):
-			attr_value = getattr(cls_ref, prop_name)
-			if issubclass(attr_value.__class__, ViewDefenition):
-				self.register_viewdef(attr_value)
-				ddoc = attr_value.design_doc
-				view_name = attr_value.name
-				setattr(cls_ref, attr_value.name, self.get_viewdef(ddoc, view_name))
-		cls_ref._couch = self
-	
-	def register_viewdef(self, viewdef):
-		""" Зарегистрировать ViewDefenition
-		Добавляет ссылку на EasyCouch в ViewDefenition для последующих вызовов view. 
-		При синхронизации добавляет view в соответствующий дизайн-документ couchdb"""
-		ddoc = viewdef.design_doc
-		view_name = viewdef.name
-		if not self.view_defs.has_key(ddoc):
-			self.view_defs[ddoc] = {}
-		if not self.view_defs[ddoc].has_key(view_name):
-			self.view_defs[ddoc][view_name] = viewdef
-		else:
-			if not viewdef.map_func is None:
-				self.get_viewdef(ddoc, view_name).map_func = viewdef.map_func
-			if not viewdef.reduce_func is None:
-				self.get_viewdef(ddoc, view_name).reduce_func = viewdef.reduce_func
-		self.get_viewdef(ddoc, view_name).couch = self
-	
-	def get(self, id):
-		doc = self.db[id]
-		doctype = doc['doctype']
-		obj = self.get_mapper(doctype).to_obj(doc)
-		return obj
-	
-	def get_mapper(self, doctype):
-		return self.registered_doctypes[doctype]
-	
-	def store(self, obj):
-		doctype = obj.doctype or obj.__class__.__name__
-		if obj._id == None:
-			obj._id = uuid.uuid4().hex
-		if obj.doctype == None:
-			obj.doctype = obj.__class__.__name__
-		data = self.get_mapper(doctype).to_json(obj)
-		self.db.save(data)
-	
-	def sync(self):
-		for doc_name, viewdefs in self.view_defs.items():
-			try:
-				doc = Document.get('_design/%s' % doc_name)
-			except ResourceNotFound:
-				doc = Document(_id = '_design/%s' % doc_name)
-			doc.language = "python"
-			doc.views = {}
-			for name, viewdef in viewdefs.items():
-				doc.views[name] = viewdef.get_code()
-			self.store(doc)
-			viewdef()
-	
-	def view(self, design, view_name, **kwarg):
-		view_name = "_design/%s/_view/%s" % (design, view_name)
-		return self.db.view(view_name, wrapper = EasyRow(self), **kwarg)
-	
-	def raw_view(self, view_path, **kwarg):
-		return self.db.view(view_path, wrapper = EasyRow(self), **kwarg)
-
-	def get_viewdef(self, design, name):
-		return self.view_defs[design][name]
-			
-import inspect
-class viewdef(object):
-	def __init__(self, design_doc):
-		self.design_doc = design_doc
-
-	def __call__(self, f):
-		lines, start_line = inspect.getsourcelines(f)
-		lines = lines[1:]
-		lines[0] = "def fun(doc):\n"
-		sources = "".join(lines)
-		fname = f.__name__
-		reduce_func = None
-		map_func = None
-		if fname[-7:] == "_reduce":
-			lines[0] = "def fun(keys, values, rereduce):\n"
-			reduce_func = "".join(lines)
-			fname = fname[:-7]
-		elif fname[-4:] == "_map":
-			map_func = sources
-			fname = fname[:-4]
-		else:
-			map_func = sources
-		vdef = ViewDefenition(self.design_doc, fname, map_func, reduce_func)
-		return vdef
+    """ EasyCouch дает возможность управлять базами данных CouchDB.
+    
+    В качестве аргументов принимает: ``url`` – адрес CouchDB, ``db_map`` – словарь, ключами
+    которого являются синонимы, а значениями – реальные имена баз данных. Это также можно
+    сделать используя метод ``map_db``. Благодаря такой ассоциации, можно получать необходимый
+    экземпляр ``Database`` через виртуальное свойство EasyCouch
+    
+    >>> couch = EasyCouch("http://localhost:5984", db_map = {'my_db': 'real_db_name'})
+    >>> type(couch.my_db)
+    <class `Database`>
+    >>> assert couch.my_db.name == "real_db_name"
+    True
+    
+    Это, в частности, позволяет использовать один и тот же сервер couchdb несколько раз. Хорошим
+    тоном будет создать синоним ``default`` для общих нужд. В любом случае остается возможность
+    получить экземпляр ``Database`` по реальному имени БД используя ``get_db``
+    """
+    def __init__(self, url = "http://localhost:5984/", db_map = {}):
+        """ Init EasyCouch.
+        
+        :param url: адрес сервера CouchDB
+        :param db_map: словарь синонимов имен баз данных. Ключ – синоним, значение – 
+            реальное имя.
+        """
+        self.api = ServerHttpApi(url)
+        self._db_map = {}
+        for alias, db in db_map.items():
+            self.map_db(alias, db)
+    
+    def map_db(self, alias, db):
+        """ Создает синоним ``alias`` для БД с именем ``db``.
+        
+        :param alias: строка
+        :param db: строка с именем БД или экземпляр :py:class:`easycouch.Database` """
+        if type(db) is str:
+            db = Database(db, couch = self)
+        assert isinstance(db, Database)
+        self._db_map[alias] = db
+    
+    def get_db(self, name):
+        """ Возвращает экземпляр :py:class:`easycouch.Database` для базы данных с именем ``name``
+        
+        :param name: строка, реальное имя БД
+        :rtype: :py:class:`easycouch.Database`
+        """
+        if not self._db_map.has_key(name):
+            self._db_map[name] = Database(name, couch = self)
+        return self._db_map[name]
+        
+    def sync(self):
+        """ Синхронизация БД и кода. В случае необходимости создает необходимые базы данных,
+        синхронизирует дизайн-документы."""
+        for alias, db in self._db_map.items():
+            db.sync()
+    
+    def __getattr__(self, name):
+        """ Возвращает :py:class:`easycouch.Database` если найден алиас с требуемым именем 
+        
+        :rtype: :py:class:`easycouch.Database`"""
+        if self._db_map.has_key(name): return self._db_map[name]
+        raise Exception("Database `%s` didn't found in db map" % name)

File easycouch/database.py

+#coding=utf-8
+import logging
+import uuid
+from .view import View, ViewResoults
+from .design import DesignDocument
+from utils import LazyDocument
+
+logger = logging.getLogger(__name__)
+
+class Database(object):
+    """ Реализует интерфейс для работы с базой данных CouchDB. Создавать самостоятельно
+    бессмысленно – удобней использовать "виртуальные" свойства :py:class:`easycouch.EasyCouch`
+    
+    По аналогии с :py:class:`EasyCouch` реализует "ваиртуальные" свойства для доступа к дизайн-
+    -документам:
+    
+    >>> couch = EasyCouch()
+    >>> db = Database("test", couch)
+    >>> my_design = DesignDocument()
+    >>> db.register_design("my_design_name", my_design)
+    >>> type(db.my_design_name)
+    <class `DesignDocument`>
+    >>> assert my_design == db.my_design_name
+    True
+    
+    :attr name: Реальное имя базы данных
+    :attr couch: Экземпляр EasyCouch которому принадлежит данная БД
+    """
+    def __init__(self, name, couch = None):
+        self.name = name
+        self.couch = couch
+        self._api = self.couch.api.get_db(self.name)
+        self._designs = {}
+        self._mappers = {}
+        self._obj_cache = {}
+    
+    def get(self, obj_id, fresh = False):
+        """ Если в качестве параметра передан список, делает запрос к _all_docs с этими ключами
+        иначе обращается к документу непосредственно и преобразует в объект по возможности.
+
+        :param obj_id: строка или список (list) строк
+        """
+        logger.debug('Get %s' % obj_id)
+        if fresh == False:
+            logger.debug('Checking cache %s' % obj_id)
+            if self._obj_cache.has_key(obj_id):
+                logger.debug('Cache used')
+                return self._obj_cache[obj_id]
+        data = self._api.get(obj_id)
+        resoult = self.make_object(data)
+        self._obj_cache[resoult._id] = resoult
+        return resoult
+    
+    def make_object(self, data):
+        """ Создает объект из json-структуры. Если отсутствует doctype – выдает исключение.
+        Определяет и обрабатывает inline-объекты, ссылки на другие объекты (оборачивает в
+        LazyDocument)
+        
+        TODO: Ссылки на объекты из другой БД
+        """
+        logger.debug(data)
+        if not data.has_key('doctype'):
+            raise Exception("'doctype' not specified in struct %s" % data)
+        for name, prop in data.items():
+            # Проходим по всем значениям, обрабатываем inline-объекты
+            if type(prop) is list:
+                for idx, item in enumerate(prop):
+                    if type(item) is dict and item.has_key('doctype'):
+                        # найден inline-объект в списке
+                        data[name][idx] = self.make_object(item)
+            elif type(prop) is dict:
+                if prop.has_key('doctype'):
+                    # найден inline-объект
+                    logger.debug("Найден inline объект")
+                    data[name] = self.make_object(prop)
+                elif prop.has_key('_id'):
+                    # Если найдена ссылка на документ
+                    data[name] = LazyDocument(self, prop['_id'])
+        return self.get_mapper(data['doctype']).to_python(data)
+    
+    def store(self, obj):
+        """ Сохраняет объект ``obj`` в базу данных или сохраняет все объекты в списке, если
+        ``obj`` таковым является.
+        
+        :param obj: object или список из них
+        :rtype: id и ревизия сохраненного документа или список этих значений аналогично obj"""
+        mapper = None
+        if type(obj) is list:
+            json = []
+            for item in obj:
+                data = self.obj2json(item)
+                if not data.has_key('_id') or data['_id'] is None:
+                    data['_id'] = uuid.uuid4().hex
+                json.append(data)
+            idrevs = self._api.batch_store(json)
+            return idrevs
+        else:
+            json = self.obj2json(obj)
+            if not json.has_key('_id') or json['_id'] is None:
+                json['_id'] = uuid.uuid4().hex
+            logger.debug("Store %s" % json)
+            _id, _rev = self._api.store(json)
+            obj._id = _id
+            self._obj_cache[_id] = obj
+            return _id, _rev
+    
+    def clear_cache(self):
+        """ Очистить кэш
+        Позволяет получить последние обновления объектов из БД """
+        self._obj_cache = {}
+    
+    def obj2json(self, obj):
+        """ Находит для объекта подходящий маппер и преобразует в простую структуру. Добавляет
+        ``doctype`` для последующего восстановления"""
+        mapper = None
+        if hasattr(obj, 'doctype'):
+            mapper = self._mappers[obj.doctype]
+        else:
+            logger.warning("Object has no `doctype`. Searching for mapper. Please define it. \
+                %s" % obj)
+            for doctype, mapper in self._mappers.items():
+                if mapper.can_map(obj):
+                    obj.doctype = doctype
+                    break;
+        d = obj.__dict__
+        if obj.doctype is None:
+            raise Exception("Fuckup")
+        for name, prop in d.items():
+            if type(prop) is list:
+                for idx, item in enumerate(prop):
+                    d[name][idx] = self.obj2json(item)
+            elif hasattr(prop, '_id'):
+                # найдена ссылка на объект
+                d[name] = {'_id': prop._id }
+            elif hasattr(prop, 'doctype'):
+                # найден inline-объект
+                d[name] = self.obj2json(prop)
+        struct = mapper.to_couch(obj)
+        struct.update({'doctype': obj.doctype })
+        return struct
+    
+    @property
+    def api(self):
+        """ Синоним api для совместимости 
+        TODO: Убрать """
+        if not self._api:
+            self._api = self.couch.api.get_db(self.name)
+        return self._api
+    
+    def register_design(self, name, design):
+        """ Регистрирует дизайн-документ в базе данных под именем ``name``. Создает исключение
+        если такое имя уже используется.
+        
+        :param name: строка, имя дизайн-документа
+        :param design: :py:class:`easycouch.DesignDocument`"""
+        if self._designs.has_key(name):
+            raise Exception("Design %s already registered" % name)
+        design.set_db(self)
+        self._designs[name] = design
+    
+    def register_mapper(self, mapper):
+        """ Регистрирует ``Mapper``, привязывая его к doctype. В случае если такой doctype уже 
+        используется – создает исключение.
+        
+        :param doctype: строка, идентификатор типа объектов
+        :param mapper: :py:class:`easycouch.Mapper` """
+        doctype = mapper.obj_cls.doctype
+        if self._mappers.has_key(doctype):
+            raise Exception("Mapper `%s` already registered" % doctype)
+        self._mappers[doctype] = mapper
+    
+    def get_mapper(self, doctype):
+        """ Возвращает ``Mapper`` для соответствующего ``doctype`` """
+        return self._mappers[doctype]
+        
+    def temp_view(self, map_func, reduce_func = "", **kwargs):
+        """ Выполнить "temp_view" с соответствующими map-reduce функциями и ``kwargs`` 
+        аргументами """
+        data = self.api.temp_view(map_func, reduce_func, **kwargs)
+        return ViewResoults(self, data)
+    
+    def execute(self, view, **kwarg):
+        """ Отправить запрос к виду на БД
+        
+        В случае если вид ассоциирован с дизайн-документом, а тот в свою очередь зарегистрирован
+        в текущей БД, отправляет запрос к виду ``view`` с параметрами ``kwarg`` и возвращает
+        результаты. В ином случае выполняет вид как временный (temp view) с теми же параметрами.
+        
+        :param view: :py:class:`easycouch.View`
+        :rtype: :py:class:`easycouch.ViewResoults` """
+        if hasattr(view, 'name') and hasattr(view, 'design'):
+            data = self.api.permanent_view(view.design.name, view.name)
+        else:
+            # Если вид не привязан к дизайну, либо дизайн не привязан к БД
+            logger.warning("Running temp view. Please sync couch for production.")
+            data = self.api.temp_view(view.map_func, view.reduce_func, **kwarg)
+        return ViewResoults(self, data)
+    
+    def create(self):
+        """ Создать базу данных"""
+        self._api.create()
+    
+    def delete(self):
+        """ Удалить базу данных """
+        self._api.delete()
+    
+    def sync(self):
+        """ Синхронизирует python представление базы данных с CouchDB:
+        
+        TODO:
+        * создает базу данных в CouchDB если её не существует
+        * синхронизирует дизайн-документы в случае их устаревания или отсутствия """
+        if not self._api.exists():
+            self.create()
+        for name, design in self._designs.items():
+            # проверяем и при необходимости обновляем дизайн-документы
+            views = {}
+            for name, view in design.views.items():
+                views[name] = {
+                    'map': view.map_func,
+                    'reduce': view.reduce_func
+                }
+            d_struct = {
+                '_id': '_design/%s' % name,
+                'views': views,
+                'language': design.language or 'javascript'
+            }
+            self._api.store(d_struct)
+    
+    def __getattr__(self, name):
+        """ Позволяет обращаться к дизайн-документам этой базы данных как к своим свойствам.
+        В случае отсутствия дизайн документа, выдает исключение."""
+        d = self.__dict__['_designs']
+        if d.has_key(name): return d[name]
+        raise Exception("Design document `%s` not found in `%s` database" % (name, self.name))

File easycouch/design.py

+#coding=utf-8
+import os, logging, subprocess
+from .view import View
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.INFO)
+
+class DesignDocument(object):
+    """ Дизайн-документ
+    
+    Дает возможность удобной работы с видами CouchDB. Вид может быть добавлен как через метод
+    ``add_view``, так и в свойствах потомка
+    
+    .. codeblock::python
+        
+        class MyDesign(DesignDocument):
+            some_view = View("function (doc) { ... }")
+            view_with_reduce = View("function (doc) {...}", "function (doc, rereduce) {...}")
+    
+    После добавления, обращаться к виду можно как к свойству объекта, но прежде дизайн-документ
+    должен быть привязан к БД.
+    
+    >>> my_design = MyDesign()
+    >>> db.register_design(my_design)
+    >>> my_design.some_view()
+    """
+    
+    def __init__(self):
+        self.views = {}
+        self.language = None
+    
+    def set_db(self, db):
+        """ Привязать дизайн к базе данных
+        
+        Создает связь между дизайном и базой данных для последующего исполнения вида """
+        self.db = db
+        for name, view in self.__class__.__dict__.iteritems():
+            if type(view) is View:
+                self.add_view(name, view)
+        
+    def add_view(self, name, view):
+        """ Добавляет вид в дизайн-документ
+        
+        :param name: строка, имя вида
+        :param view: :py:class:`easycouch.View`
+        """
+        if self.views.has_key(name):
+            raise Exception("View %s already registered" % name)
+        view.set_design(self)
+        self.views[name] = view
+    
+    def sources_from_path(self, path):
+        """ Загружает исходные коды вида из директории
+        Директория должна быть организована в структуру, где для каждого вида создана отдельная
+        директория с файлами map.js и reduce.js (не обязательно)"""
+        logging.info("Loading design data from path %s" % path)
+        for dirname, dirnames, filenames in os.walk(path):
+            for view_dir in dirnames:
+                logger.debug("Found view `%s` in design `%s`" % (view_dir, self))
+                if not hasattr(self, view_dir):
+                    setattr(self, view_dir, View())
+                view = getattr(self, view_dir)
+                p = os.path.join(dirname, view_dir)
+                map_js = os.path.join(p, 'map.js')
+                reduce_js = os.path.join(p, 'reduce.js')
+                if os.path.exists(map_js):
+                    logger.debug(".. found map.js")
+                    view.map_func = self._load_js_file(map_js)
+                if os.path.exists(reduce_js):
+                    logger.debug(".. found reduce.js")
+                    view.reduce_func = self._load_js_file(reduce_js)
+    
+    def _load_js_file(self, filename):
+        """ Возвращает содержимое js файла и по возможности проводит его проверку jslint
+        """
+        logger.info(">>>>> JSL output for %s ========" % filename)
+        subprocess.call("jsl --process %s" % filename, shell=True)
+        data = open(filename).read()
+        logger.info("<<<<< END")
+        return data
+    
+    def __getattr__(self, name):
+        if self.__dict__['views'].has_key(name):
+            return self.__dict__['views'][name]
+        raise Exception("View %s not found" % name)

File easycouch/document.py

-#coding=utf-8
-import uuid
-import logging
-from properties import BaseProperty
-from utils import LazyDocument
-
-class MutationStore(object):
-	""" Объект, задачей которого является отдельное хранение свойств класса и виртуальных свойств
-	(в нашем случае документа) и гибридный доступ к ним.
-	
-	Это происходит за счет сохранения состояния: все свойства созданные после сохранения будут
-	считаться виртуальными и будут сохранены отдельно.
-	"""
-	_state_stored = False
-	
-	def _store_state(self):
-		""" Сохранить теущее состояние
-		Запоминаются все задействованные свойства объекта. С этого момента любые новые
-		свойства будут восприниматься как 'виртуальные' при __setattr__ и __getattr__"""
-		if not self._state_stored:
-			self._data = {}
-			self._state = dir(self)
-			self._state_stored = True
-		else:
-			raise Exception("State already stored")
-	
-	def _get_state_diff(self):
-		""" Возвращает изменения в состоянии объекта
-		"""
-		return self._data
-	
-	def _remove_from_state(self, names):
-		if not isinstance(names, list):
-			names = [names,]
-		for i in names:
-			self._state.remove(i)
-	
-	def __setattr__(self, name, value):
-		if self._state_stored and not name in self._state:
-			self._data[name] = value
-		else:
-			object.__setattr__(self, name, value)
-	
-	def __getattr__(self, name):
-		if self._state_stored and self._data.has_key(name):
-			if isinstance(self._data[name], LazyDocument):
-				self._data[name] = self._data[name]()
-				print "found lazy replaced with ", self._data[name]
-			return self._data[name]
-		else:
-			return None
-
-class InlineDocument(MutationStore):
-	def __init__(self, **kwargs):
-		self._mapper = self._couch.get_mapper(self.__class__.__name__)
-		self._store_state()
-		self.doctype = self.__class__.__name__
-		for name, value in kwargs.items():
-			setattr(self, name, value)
-		for name, prop in self._mapper.properties.items():
-			value = getattr(self, name)
-			if isinstance(value, BaseProperty) or value is None:
-				setattr(self, name, prop.get_default())
-		#self._remove_from_state(self._mapper.properties.keys())
-	
-	def _get_plain_data(self):
-		return self._get_state_diff()
-
-	
-class Document(InlineDocument):
-	def store(self):
-		self._couch.store(self)
-	
-	@classmethod
-	def get(cls, id):
-		return cls._couch.get(id)

File easycouch/http.py

+#coding=utf-8
+import logging
+import ujson as json
+from urllib3 import connection_from_url
+from urllib import urlencode
+
+logger = logging.getLogger(__name__)
+
+class ServerHttpApi(object):
+	def __init__(self, url = "http://localhost:5984/"):
+		self.http_pool = connection_from_url(url)
+		self.db_apis = {}
+	
+	def all_dbs(self):
+		""" Возвращает список имен всех баз данных """
+		r = self.http_pool.request('GET', '/_all_dbs')
+		assert r.status == 200
+		return json.decode(r.data)
+	
+	def get_db(self, db_name):
+		""" Возвращает api базы данных (экземпляр DatabaseHttpApi) для бд с именем db_name
+		"""
+		if not self.db_apis.has_key(db_name):
+			self.db_apis[db_name] = DatabaseHttpApi(db_name, self)
+		return self.db_apis[db_name]
+	
+	def active_tasks(self):
+		""" Возвращает список активных задач """
+		raise NotImplemented
+	
+	def replicate(self):
+		""" Начать или остановить репликацию """
+		raise NotImplemented
+	
+	def uuids(self):
+		""" Возвращает список уникальных uuid """
+		raise NotImplemented
+	
+	def restart(self):
+		""" Перезапустить сервер, требуются привелегии администратора """
+		raise NotImplemented
+	
+	def stats(self):
+		""" Возвращает статистику """
+		raise NotImplemented
+	
+	def log(self):
+		""" Возвращает лог, требуются привелегии администратора """
+		return self.get('/_log').data
+	
+	def get(self, uri, **http_pool_kw):
+		return self.http_pool.request('GET', uri, **http_pool_kw)
+
+	def post_json(self, uri, data, **http_pool_kw):
+		"""Make post json request to db"""
+		headers = {'Content-Type': 'application/json',}
+		if not http_pool_kw.has_key('headers'):
+			http_pool_kw['headers'] = {}
+		http_pool_kw['headers'].update(headers)
+		return self.http_pool.urlopen('POST', uri, json.encode(data), **http_pool_kw)
+
+	def put(self, uri, **http_pool_kw):
+		"""Make put request to db"""
+		return self.http_pool.request('PUT', uri, **http_pool_kw)
+	
+	def delete(self, uri, **http_pool_kw):
+		"""Make DELETE request to db"""
+		return self.http_pool.request('DELETE', uri, **http_pool_kw)
+
+	def head(self, uri, **http_pool_kw):
+		"""Make head request to db"""
+		return self.http_pool.request('HEAD', uri, **http_pool_kw)
+
+class DatabaseHttpApi(object):
+	def __init__(self, name, server):
+		self.name = name
+		self.server = server
+	
+	def store(self, data):
+		r = self.server.post_json('/%s' % self.name, data)
+		if r.status != 201:
+			logger.error("Save doc %s failed" % data)
+			raise Exception("Can't save document %s. Error: %s" % (data, r.data))
+		r = json.decode(r.data)
+		return r['id'], r['rev']
+	
+	def batch_store(self, data):
+	    r = self.server.post_json('/%s/_bulk_docs' % self.name, {'docs': data})
+	    if r.status != 201:
+	        logger.debug(data)
+	        raise Exception("Can't save documents")
+	    r = json.decode(r.data)
+	    return r
+	
+	def create(self):
+		r = self.server.put('/%s' % self.name)
+		if r.status != 201:
+			data = json.decode(r.data)
+			if data['error'] == "file_exists":
+				raise Exception("Database `%s` already exists" % self.name)
+			raise Exception("Can't create database `%s`. Reason: %s" % (self.name, data['reason']))
+		return r.status == 201
+	
+	def delete(self):
+		r = self.server.delete('/%s/' % self.name)
+		if r.status != 200:
+		    raise Exception("Can't delete database `%s` %s" % (self.name, json.decode(r.data)))
+		return r.status == 200
+	
+	def get(self, id_or_list):
+		""" Возвращает документ по id, или если аргументом передан список, возвращает
+		документ для каждого id из списка """
+		if type(id_or_list) is list:
+			return self.all_docs(keys=id_or_list)
+		r = self.server.get('/%s/%s' % (self.name, id_or_list))
+		if r.status != 200:
+			raise Exception("Can't get document %s: %s" % (id_or_list, r.data))
+		logger.debug(r.data)
+		return json.decode(r.data)
+	
+	def temp_view(self, map_func, reduce_func = "", **kwargs):
+		fields = {
+			'map': map_func,
+		}
+		if reduce_func != "":
+			fields['reduce'] = reduce_func
+		q = urlencode(kwargs)
+		headers = {'Content-Type': 'application/json',}
+		r = self.server.post_json('/%s/_temp_view?%s' % (self.name, q), data = fields, headers = headers)
+		logger.debug("Temp view data: %s, %s", r.status, r.data)
+		if r.status != 200:
+			logger.debug(self.server.log())
+			raise ViewException(r)
+		return json.decode(r.data)
+	
+	def view(self, view_name):
+		pass
+	
+	def exists(self):
+	    """ Возвращает True если БД существует """
+	    r = self.server.head('/%s' % self.name)
+	    if r.status == 200:
+	        return True
+	    elif r.status == 404:
+	        return False
+	    else:
+	        raise Exception("Unexpected exception in `%s`: %s" % (self.name,r.data))
+
+class ViewException(Exception):
+	"""docstring for ViewException"""
+	def __init__(self, resp):
+		super(ViewException, self).__init__("Can't execute view: %s.\n Couch log: %s" % (resp.data, self.server.log()))
+		

File easycouch/mapper.py

+#coding=utf-8
+import logging
+from .properties import BaseProperty
+
+logger = logging.getLogger(__name__)
+#logger.setLevel(logging.DEBUG)
+
+class Mapper(object):
+    """ mapper служит для преобразования python-объектов в документы couchdb """
+    def __init__(self, obj_cls, exclude=[]):
+        """ Создает маппер
+        doctype – при сохранении объектов класса `obj_cls`
+        obj_cls – ссылка на класс для которого этот маппер должен применяться"""
+        self.obj_cls = obj_cls
+        self.exclude = exclude
+        self.properties = {}
+        for name in dir(self.__class__):
+            prop = getattr(self.__class__, name)
+            if isinstance(prop, BaseProperty):
+                self.properties[name] = prop
+    
+    def to_python(self, json):
+        logger.debug("Start converting json struct to python object\n%s" % json)
+        resoult = self.obj_cls()
+        for key, value in json.items():
+            setattr(resoult, key, value)
+        logger.debug(dir(self))
+        for name in dir(type(self)):
+            prop = getattr(self, name)
+            if isinstance(prop, BaseProperty):
+                logger.debug("Found defined property %s" % name)
+                setattr(resoult, name, prop.from_db(json[name]))
+        return resoult
+    
+    def to_couch(self, obj):
+        logger.debug("====== Mapping %s to database =======" % obj)
+        logger.debug("With mapper %s" % type(self))
+        resoult = {}
+        struct = obj.__dict__
+        for key, value in struct.items():
+            if not key in self.exclude:
+                resoult[key] = value
+        for name in dir(type(self)):
+            prop = getattr(self, name)
+            if isinstance(prop, BaseProperty):
+                logger.debug("Found property %s" % name)
+                if not resoult.has_key(name):
+                    default = prop.get_default()
+                    setattr(obj, name, default)
+                    resoult[name] = default
+                resoult[name] = prop.to_db(resoult[name])
+        logger.debug(resoult)
+        return resoult
+    
+    def can_map(self, obj):
+        if obj.__class__ == self.obj_cls:
+            return True
+        return False
+        

File easycouch/properties.py

 from dateutil import parser
 
 class DateTimeProperty(BaseProperty):
-	def __init__(self, default = None, auto_now = False):
-		if auto_now:
+	def __init__(self, default = None, auto_now = False, auto_now_add = False):
+		if auto_now or auto_now_add:
 			default = datetime.datetime.now
 		self.default = default
 		

File easycouch/utils.py

 	Подставляется на место конструкций {'_id': '...'} и при обращении возвращает реальный
 	объект с этим _id. Это позволит в дальнейшем сделать join'ы
 	"""
-	def __init__(self, couch, obj_id):
-		self.couch = couch
+	def __init__(self, db, obj_id):
+		self.db = db
 		self.obj_id = obj_id
 	
 	def __call__(self):
-		print "Lazy opened", self.obj_id
-		return self.couch.get(self.obj_id)
+		return self.db.get(self.obj_id)

File easycouch/view.py

 #coding=utf-8
-import inspect
+import logging
 
-def row_wrapper(row):
-	print "Row: ", row
+logger = logging.getLogger(__name__)
 
-class EasyRow(object):
-	def __init__(self, couch):
-		self.couch = couch
-	
-	def __call__(self, row):
-		doc_data = row['value']
-		if row.has_key('doc'):
-			doc_data = row['doc']
-		if isinstance(doc_data, dict) and doc_data.has_key('doctype'):
-			doctype = doc_data['doctype']
-			mapper = self.couch.get_mapper(doctype)
-			doc = mapper.to_obj(doc_data)
-		else:
-			doc = row['value']
-		if row.has_key('doc') and row['value'] != None:
-			return row['key'], doc, row['value']
-		return row['key'], doc
+class View(dict):
+    """ Вид CouchDB
+    
+    По сути лишь содержит в себе всю необходимую для выполнения запроса к виду информацию. При 
+    вызове (``__call__``) отправляет запрос к виду, если определен ``design``. Иначе необходимо
+    выполнять как временный вид на БД:
+    
+    >>> my_view = View("function(doc) { ... }")
+    >>> res1 = db.execute(my_view, include_docs = True)
+    >>> my_design = DesignDocument()
+    >>> db.register_design("my_design", my_design)
+    >>> my_design.add_view("my_view", my_view)
+    >>> res2 = my_design.my_view(include_docs = True)
+    
+    :attr map_func: строка, map-функция
+    :attr reduce_func: строка, reduce-функция
+    :attr language: строка, язык функций
+    :attr design: :py:class:`easycouch.DesignDocument`, дизайн-документ к которому вид привязан
+    """
+    def __init__(self, map_func = "", reduce_func = "", language = "javascript", design = None):
+        """ """
+        super(View, self).__init__()
+        self.map_func = map_func
+        self.reduce_func = reduce_func
+    
+    def set_design(self, design):
+        self.design = design
+    
+    def __call__(self, db = None, **kwarg):
+        """ Запрос к виду """
+        return self.design.db.execute(self, **kwarg)
 
-class ViewDefenition(object):
-	def __init__(self, design_doc, name, map_func = None, reduce_func = None):
-		self.design_doc = design_doc
-		self.name = name
-		self.map_func = map_func
-		self.reduce_func = reduce_func
-	
-	def __call__(self, **kwarg):
-		return self.couch.view(self.design_doc, self.name, **kwarg)
-	
-	def get_code(self):
-		res = {}
-		if not self.map_func is None:
-			res['map'] = self.map_func
-		if not self.reduce_func is None:
-			res['reduce'] = self.reduce_func
-		return res
+class ViewResoults(object):
+    """
+    ----
+    TODO
+    ----
+    * фильтры (например, по классу объектов. Остальные данные должны добавляться в кэш)
+    
+    """
+    def __init__(self, db, data, filter_cls=[]):
+        logger.debug("ViewResoults: %s" % data)
+        self.flat_rows = data['rows']
+        self.total_rows = data['total_rows']
+        self.offset = data['offset']
+        self.db = db
+    
+    def __iter__(self):
+        for item in self.flat_rows:
+            yield item
+    
+    def values(self):
+        for item in self.flat_rows:
+            key, value = item['key'], item['value']
+            value = self._parse_struct(value)
+            yield key, value
+    
+    def _parse_struct(self, value):
+        if type(value) is dict:
+            if value.has_key('doctype'):
+                value = self.db.make_object(doc)
+            else:
+                for key, v in value:
+                    value[key] = self._parse_struct(v)
+        elif type(value) is list:
+            for i, v in enumerate(value):
+                value[i] = self._parse_struct(v)
+        return value
+    
+    def objects(self):
+        """ """
+        for item in self.flat_rows:
+            yield item['key'], self.db.make_object(item['doc'])
+    
+    def items(self):
+        for item in self.flat_rows:
+            if item.has_key('doc'):
+                yield item['key'], item['value'], self.db.make_object(item['doc'])
+            else:
+                yield item['key'], item['value']
+    
+    def __len__(self):
+        return len(self.flat_rows)

File html_doc/.buildinfo

+# Sphinx build info version 1
+# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: f3d848428b3e08ad3d397099d2963fbc
+tags: fbb0d17656682115ca4d033fb2f83ba1

File html_doc/.doctrees/environment.pickle

Binary file added.

File html_doc/.doctrees/index.doctree

Binary file added.

File html_doc/_modules/easycouch.html

+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    
+    <title>easycouch &mdash; easycouch 0.3dev documentation</title>
+    
+    <link rel="stylesheet" href="../_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+    
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+        URL_ROOT:    '../',
+        VERSION:     '0.3dev',
+        COLLAPSE_INDEX: false,
+        FILE_SUFFIX: '.html',
+        HAS_SOURCE:  true
+      };
+    </script>
+    <script type="text/javascript" src="../_static/jquery.js"></script>
+    <script type="text/javascript" src="../_static/underscore.js"></script>
+    <script type="text/javascript" src="../_static/doctools.js"></script>
+    <link rel="top" title="easycouch 0.3dev documentation" href="../index.html" />
+    <link rel="up" title="Module code" href="index.html" /> 
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="../genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="../py-modindex.html" title="Python Module Index"
+             >modules</a> |</li>
+        <li><a href="../index.html">easycouch 0.3dev documentation</a> &raquo;</li>
+          <li><a href="index.html" accesskey="U">Module code</a> &raquo;</li> 
+      </ul>
+    </div>  
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  <h1>Source code for easycouch</h1><div class="highlight"><pre>
+<span class="kn">from</span> <span class="nn">couch</span> <span class="kn">import</span> <span class="n">EasyCouch</span>
+<span class="kn">from</span> <span class="nn">database</span> <span class="kn">import</span> <span class="n">EasyDatabase</span>
+<span class="kn">from</span> <span class="nn">design</span> <span class="kn">import</span> <span class="n">DesignDocument</span>
+<span class="kn">from</span> <span class="nn">view</span> <span class="kn">import</span> <span class="n">EasyView</span><span class="p">,</span> <span class="n">ViewResoults</span>
+<span class="kn">from</span> <span class="nn">mapper</span> <span class="kn">import</span> <span class="n">EasyMapper</span>
+</pre></div>
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+  <h3>Quick search</h3>
+    <form class="search" action="../search.html" method="get">
+      <input type="text" name="q" />
+      <input type="submit" value="Go" />
+      <input type="hidden" name="check_keywords" value="yes" />
+      <input type="hidden" name="area" value="default" />
+    </form>
+    <p class="searchtip" style="font-size: 90%">
+    Enter search terms or a module, class or function name.
+    </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="../genindex.html" title="General Index"
+             >index</a></li>
+        <li class="right" >
+          <a href="../py-modindex.html" title="Python Module Index"
+             >modules</a> |</li>
+        <li><a href="../index.html">easycouch 0.3dev documentation</a> &raquo;</li>
+          <li><a href="index.html" >Module code</a> &raquo;</li> 
+      </ul>
+    </div>
+    <div class="footer">
+        &copy; Copyright 2012, Roman Tolkachyov.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.2.
+    </div>
+  </body>
+</html>

File html_doc/_modules/easycouch/properties.html

+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    
+    <title>easycouch.properties &mdash; easycouch 0.3dev documentation</title>
+    
+    <link rel="stylesheet" href="../../_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
+    
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+        URL_ROOT:    '../../',
+        VERSION:     '0.3dev',
+        COLLAPSE_INDEX: false,
+        FILE_SUFFIX: '.html',
+        HAS_SOURCE:  true
+      };
+    </script>
+    <script type="text/javascript" src="../../_static/jquery.js"></script>
+    <script type="text/javascript" src="../../_static/underscore.js"></script>
+    <script type="text/javascript" src="../../_static/doctools.js"></script>
+    <link rel="top" title="easycouch 0.3dev documentation" href="../../index.html" />
+    <link rel="up" title="easycouch" href="../easycouch.html" /> 
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="../../genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="../../py-modindex.html" title="Python Module Index"
+             >modules</a> |</li>
+        <li><a href="../../index.html">easycouch 0.3dev documentation</a> &raquo;</li>
+          <li><a href="../index.html" >Module code</a> &raquo;</li>
+          <li><a href="../easycouch.html" accesskey="U">easycouch</a> &raquo;</li> 
+      </ul>
+    </div>  
+
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  <h1>Source code for easycouch.properties</h1><div class="highlight"><pre>
+<div class="viewcode-block" id="BaseProperty"><a class="viewcode-back" href="../../index.html#easycouch.properties.BaseProperty">[docs]</a><span class="k">class</span> <span class="nc">BaseProperty</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+	<span class="k">def</span> <span class="nf">to_db</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
+		<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
+	
+	<span class="k">def</span> <span class="nf">from_db</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
+		<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
+		</div>
+<span class="kn">import</span> <span class="nn">datetime</span>
+<span class="kn">from</span> <span class="nn">dateutil</span> <span class="kn">import</span> <span class="n">parser</span>
+
+<div class="viewcode-block" id="DateTimeProperty"><a class="viewcode-back" href="../../index.html#easycouch.properties.DateTimeProperty">[docs]</a><span class="k">class</span> <span class="nc">DateTimeProperty</span><span class="p">(</span><span class="n">BaseProperty</span><span class="p">):</span>
+	<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default</span> <span class="o">=</span> <span class="bp">None</span><span class="p">,</span> <span class="n">auto_now</span> <span class="o">=</span> <span class="bp">False</span><span class="p">):</span>
+		<span class="k">if</span> <span class="n">auto_now</span><span class="p">:</span>
+			<span class="n">default</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span>
+		<span class="bp">self</span><span class="o">.</span><span class="n">default</span> <span class="o">=</span> <span class="n">default</span>
+		
+	<span class="k">def</span> <span class="nf">from_db</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
+		<span class="n">res</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
+		<span class="k">return</span> <span class="n">res</span>
+
+	<span class="k">def</span> <span class="nf">to_db</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
+		<span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">isoformat</span><span class="p">()</span>
+	
+	<span class="k">def</span> <span class="nf">get_default</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+		<span class="k">if</span> <span class="nb">callable</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">default</span><span class="p">):</span>
+			<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">default</span><span class="p">()</span>
+		<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">default</span></div>
+</pre></div>
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+  <h3>Quick search</h3>
+    <form class="search" action="../../search.html" method="get">
+      <input type="text" name="q" />
+      <input type="submit" value="Go" />
+      <input type="hidden" name="check_keywords" value="yes" />
+      <input type="hidden" name="area" value="default" />
+    </form>
+    <p class="searchtip" style="font-size: 90%">
+    Enter search terms or a module, class or function name.
+    </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">