pip install django-pagetree_export
This is a library, not an app; so you'll have to integrate it into your Django project manually. The interesting functions are:
from pagetree_export.exportimport import export_zip
from pagetree_export.exportimport import import_zip
They do what you'd expect: export a pagetree hierarchy to a zipfile, and import a zipfile to a pagetree hierarchy. You can use them in views like:
hierarchy = pagetree.helpers.get_hierarchy(name="main")
filename = export_zip(hierarchy)
with open(filename) as zipfile:
resp = HttpResponse(zipfile.read())
resp['Content-Disposition'] = "attachment; filename=pagetree-export.zip"
os.unlink(filename)
return resp
Note that export_zip
returns a filename which can be used to read
and return the contents of a zipfile. The file it creates is a
temporary file (i.e. it lives under /tmp) so you don't have to worry
about it too much, but you should remember to be polite and remove the
tempfile from the system before returning the HTTP response.
And here's the corresponding import:
from zipfile import ZipFile
file = request.FILES['file']
zipfile = ZipFile(file)
new_hierarchy = import_zip(zipfile)
The import_zip
function takes a zipfile.ZipFile
which is an object
in the Python standard library. You can get a ZipFile object from a
Django file upload by wrapping the object, as shown in the sample
above.
When importing a hierarchy from a zipfile, the system prevents
duplicate hierarchies with identical names, and duplicate root nodes
within a hierarchy, by using get_or_create
rather than create
. If
a matching hierarchy (or a root node) already exists, its attributes
will be replaced with the attributes described in the imported
zipfile.
If you're doing export/import on a pair of simple pagetree sites with only one hierarchy (e.g. https://github.com/ccnmtl/Diabeaters) you probably don't need to worry about this -- you'll want the imported-site to match the exported-site exactly.
However, if you're integrating this library into a complex pagetree
project which manages multiple hierarchies in a single installation,
you probably do need to worry about this: you're using export/import
as a copy function within a single site. The way to worry about this
is to override the import-bundle's knowledge of its own hierarchy
name, by passing a hierarchy_name
argument to the import_zip
function. Here's an example, using the hostname of the current
request as the hierarchy identifier:
file = request.FILES['file']
zipfile = ZipFile(file)
# If we exported the morx.com site, and we are now
# visiting http://fleem.com/import/, we don't want
# to touch the morx.com hierarchy -- instead we want
# to import the bundle to the fleem.com hierarchy.
hierarchy_name = request.get_host()
hierarchy = import_zip(zipfile, hierarchy_name)
You can plug in new exporters and importers for particular types of pageblocks.
An export function takes a PageBlock, an open writable XML file, and an open writable zipfile; it should at minimum write to the XML file at its current position, and may also want to write extra files to the zipfile.
An import function takes an xml.etree node and an open readable zipfile; it should at minimum parse the node (and its children) to generate a new PageBlock, and may also want to read extra files from the zipfile. It should return the newly-created PageBlock.
You will need to register your exporter and importer functions like so:
from pagetree_export import register
register(MyPageBlockClass, "mypageblockclass",
my_export_fn, my_import_fn)
If you prefer, you can organize your code with classes, and register the classes with a decorator. The contract for these classes is:
from pagetree_export import register_class
@register_class
class MyExporter(object):
block_class = MyPageBlockClass
identifier = "mypageblockclass"
def exporter(self, block, xmlfile, zipfile):
""" write to the file, return nothing """
def importer(self, etree_node, zipfile):
new_block = MyPageBlockClass(**some_attributes)
new_block.save()
return new_block
The exporter
and importer
methods are both optional; you can omit
one or the other of them to register an exporter with no corresponding
importer, or vice versa. The block_class
and identifier
attributes are not optional.