Internal API

It is important to read the Concepts document before studying the API, otherwise you will not understand what I’m talking about.

In the description of the database models below, it goes without saying that all models have an id field, which is usually not mentioned unless there is something unusual about it.

The Entry model

class zadig.core.models.Entry(path)

Stores an entry. It is always a subclass that is used, rather than Entry itself. The constructor of the subclass creates a new entry at the specified path. The parent entry must already exist and the logged on user must have appropriate permissions on the parent entry; otherwise, an exception is raised. If other entries with the same parent entry exist, the entry is added as the last sibling.

Entry objects have the following database attributes:


A string with the filename of the base template. If empty, it means to use the same template as the parent entry; see base_template for more information.


A foreign key to Entry (that is, to self). It indicates which entry contains the entry. This attribute can be null; there is one and only one entry that has a null container, and it is the root entry.

The related_name of this attribute is all_subentries. Therefore, if you have an entry myentry, then myentry.all_subentries is the entries contained in myentry. You should not, however, use all_subentries, unless you have a good reason to do so; instead, use subentries(), which checks permissions.


A foreign key to MultilingualGroup, indicating the multilingual group, if any, to which the entry belongs.


The name of the entry.


A foreign key to django.contrib.auth.models.User, indicating the owner of the entry.


The sequence of the entry in its container: 1 for the first entry, 2 for the second entry, and so on.


A foreign key to State, indicating the state of the entry.


A foreign key pointing to the current vobject, that is to the vobject that has the maximum version_number) for the entry.

This field is redundant, but has to be there to enable some queries to run properly; see these two posts for more information. You should never attempt to set it; Zadig automatically updates it upon saving a new vobject.

Entry has the following class attributes:


This manager returns all objects. This is a copy of the default django manager. In general, you should avoid using this; you should instead use objects, which is an EntryManager.

Entry objects also have the following attributes, properties and methods:


This read-only property returns the absolute URI for the show action of the entry.

add_details(vobject, form)

The action_edit() method is implemented in Entry and not in its subclasses. When creating a new vobject as a result of edit form submission, it doesn’t know how to process all attributes submitted with the form. It therefore processes only those it can, and then calls this method (which should be implemented in the subclass), which should process any form attributes special to the subclass and modify vobject accordingly.


A list with the alternative language entries. The order in this list is the order with which languages are listed in ZADIG_LANGUAGES.


A string with the filename the base template the entry should use. This is the base template used to render the page (the base template inherited by the actual template). The result is btemplate, except if this is empty, in which case the parent’s base template is used; if the top level entry’s btemplate attribute is also empty, then base.html is returned.

classmethod can_be_contained(parent)

Not all kinds of objects can contain all kinds of objects; for example, only blog objects can contain blog post objects; and blog post objects cannot contain other pages. Each Entry subclass has the can_contain() method and the can_be_contained() class method that indicate what kind of objects can be added as subobjects.


In this text, we use “subobjects” in the containment sense; that is, an object can contain subobjects. We use “subclass” in the usual hierarchical sense.

The can_contain() method indicates whether the object is willing to contain a child subobject, where child is an Entry subclass; and the can_be_contained() class method indicates whether the class is willing to create a new subobject of parent, where parent is an Entry subclass. You should only call the later, because it calls the former itself, whereas the former does not call the latter and therefore may miss additional restrictions.

The toplevel can_contain() returns True, provided that the user has appropriate permissions. The toplevel can_be_contained() returns parent.can_contain(cls).


Check whether entry is contained, directly or indirectly, in self. Returns True if self is the parent of entry, or the parent of its parent, or any ancenstor, and False otherwise.


Return a django.http.HttpResponse object with the contents of the entry.


The date the entry was created, i.e. the date of its first vobject. See also last_modification_date.


Entry is always subclassed. If you get a grip on an object of type Entry when in fact you need that same object but in its descendant class, and you don’t know which subclass it is, use this property, which gives you the same object but in the proper subclass.

edit_subform(data=None, files=None, new=False)

When the object is being edited, a form is shown to the user; this consists of items that are common to all Entry subclasses, plus items that are specific to the subclass. The items that are specific to the subclass are the edit subform, and they are a Django form, i.e. a django.forms.Form subclass. edit_subform() returns that form. If data (and optionally files) are specified, it returns a bound form (data=request.POST and files=request.FILES is normally specified upon a POST request); otherwise, if new is True, we are creating a new entry (normally an empty form should be returned); otherwise, we are editing an existing entry (normally an unbound form with the last vobject data as initial data should be returned).

The toplevel edit_subform() returns an empty form. Subclasses should redefine as needed.


Return a django.http.HttpResponse object. Depending on the contents of the request object, it either processes a submitted form (either modifying the entry or finding an error in the submitted form) and returns the response, or it returns a page with a form for editing the entry. self must be an Entry subclass.

If new is True, it means that the entry does not exist yet but is in the process of being created. When calling in this manner, self must be an Entry subclass, and container must have been set to the entry of which the new entry will be a child; the other attributes are irrelevant. Depending on the contents of the request object, the method will then either process a submitted form (either creating the entry or finding an error in the submitted form) and return the response, or return a page with a (mostly empty) form for filling in the entry.


Return the entry’s vobject; actually returns a VObject descendant. If version_number is not specified, it returns the latest vobject. An exception is raised if the user does not have permission to view the vobject.


The last modification date of the entry, i.e. the date of its latest vobject.


Return a django.http.HttpResponse object with the history of the entry.


Move the entry from its current container to target_entry, which will be the new container. Also create a new redirection entry at the old location. Verifies permissions to do all that.


Return the owner full name, if available, otherwise the username.


This read-only property returns the URL path to the entry, not including a leading or trailing slash. See also spath and absolute_uri.


Return the permissions the logged on user has on the entry.


Return a django.http.HttpResponse object with the permissions of the entry.


A list of State objects, which are the possible states to which the workflow allows the current user to move the entry to.

process_edit_subform(vobject, subform)

After the user submits the edit form, the subform returned by edit_subform() must be processed. This method receives the newly created vobject and the submitted subform and processes as needed. The top level method does nothing; it is subclasses that must define how the processing is done.


Rename the entry to the specified new name; it also creates a new redirection entry with the old name.

reorder(source_seq, target_seq)

Move the subentry with seq=source_seq before subentry with seq=target_seq. source_seq and target_seq are integers. The function changes the order of the children of the entry. The child that has seq=source_seq is moved before the child that has seq=target_seq, unless target_seq is one more than the number of children, in which case the child is moved to the end. The other children are renumbered as needed (i.e. their seq is modified accordingly). Raises an exception if source_seq or target_seq are inappropriate; for example, if source_seq is larger than the number of children, or if source_seq and target_seq are both the same number. Also raises an exception if the user does not have permission to do this.


A copy of the request object.


Set multilingual_group so that the entry specified by altpath is an alternative language entry. altpath is a path to another entry.

Note that this method will rarely, if ever return an error: it tries to be smart and do the right thing. Maybe it’s too smart. One case where it raises an exception is if this entry and the other entry are in the same language or they don’t have a language specified.

If the entry specified by altpath does not exist or is inaccessible, the method does nothing.

If one of the entries involved (say A) is already in a multilingual group, and the other entry (say B) is not, it adds B to the multilingual group of A, unless there is already an entry with that language in the group; in that case, it removes A from the group and creates a new multilingual group for A and B.

If both entries are already in multilingual groups, it attempts to join these groups together; but again, if there are language conflicts, it removes one or both of the entries from their old groups as needed.


This read-only property returns the full relative URL path to the entry, starting and ending in a slash. (This is more complicated than just prefixing and suffixing path with a slash, because then the root entry would be two slashes instead of one.) See also path and absolute_uri.


A query set of Entry objects, which are the subentries which the user has permission to search, in order.


This class attribute is the name of the template for editing the entry. Frequently the inherited value is OK.


True if the current user has either edit or admin permission on the entry. Primarily used by the template to check whether to show the editing buttons.


The class name, such as “PageEntry”, or “ImageEntry”.


This is a class attribute, with a translatable, human readable name for the type, such as “Page” or “Image”.


When called on an entry whose last vobject is a deletion_mark, it creates an additional vobject identical in content to the vobject before undeletion, thereby reverting to that last vobject.

The default Entry manager

class zadig.core.models.EntryManager

Three things are important about the default Entry manager (accessible via Entry.objects):

  1. The manager automatically filters objects and does not return those for which the user does not have search permission (see request object to understand how this is done), or objects which are deleted (see deletion_mark).
  2. The manager is inherited by subclasses, contrary to normal django practice.
  3. The manager has a few additional methods, described below.

See also zadig.core.models.Entry.all_objects.


Return the entry at the specified path; raise django.http.Http404 if such an entry does not exist or the user does not have permissions to view it (you normally don’t need to handle that exception: it will result in a 404 page).

Generally you should much prefer to use the above method when retrieving Entries, because it will take care of permissions at a low level.


Return a query set that does not contain items that are represented in the default query set in a more appropriate language. For example:


The resulting query set will not contain entries which are in the same multilingual group. If two entries are one the equivalent of the other in another language, only one of them will be included. If one of them is in the effective language, then it will be included, and the other will be excluded; if both are in a language which is not the effective language, then one of them will be included at random.

The VObject model

class zadig.core.models.VObject

This model is the parent of models that inherit it using multi-table inheritance, and stores a vobject. This model does not actually store the content of the vobject; this is stored by the descendant model. VObject provides the following attributes and methods:


Foreign key to Entry.


An integer.


The date in which the vobject has been created.


If this boolean attribute is True, then this vobject is a deletion mark, which means that the entry was marked as deleted when this vobject was created. The vobject’s metatags and content are irrelevant in this case.


A foreign key to Language designating the language of the vobject.


VObject is always subclassed. If you get a grip on an object of type VObject when in fact you need that same object but in its descendant class, and you don’t know which subclass it is, use this property, which gives you the same object but in the proper subclass.


A copy of the request object.


Return a django.http.HttpResponse object that shows the vobject.


Returns a django.http.HttpResponse object showing the info for the vobject.


Used internally. When a vobject is deleted (i.e. has deletion_mark set), then this method is called instead of its other actions This method decides if it should return a response or whether it should raise a django.http.Http404.


Create and save an exact copy of the vobject; the only thing that is different in the newly created vobject is its id and date. Used for reverting an entry to an old vobject.

VObject has a custom manager:


objects is similar to the default manager, except that it has an additional method:

get_by_path(path[, version_number])

Return the vobject that corresponds to the entry at the specified path. If version_number is not specified, it returns the latest vobject. django.http.Http404 is raised if the entry does not exist or the user does not have permissions to view it (you normally don’t need to handle that exception: it will result in a 404 page).

Other core models

class zadig.core.models.EntryPermission

Permissions assigned to the entry besides those assigned to its state. Has three attributes: entry, lentity and permission, all foreign keys to the respective model.

class zadig.core.models.Language

Contains languages. It has only one field, id, storing the language id, as a 5-character long string, in the form “en” or “en_us” etc.

class zadig.core.models.Permission

A lookup that lists permissions: “view”, “edit”, “admin”, “search”, “delete”. Has only a descr attribute.

class zadig.core.models.Lentity

A Lentity represents either a user or a group, and is used whenever any of the two can be used; for example, a certain permission can be given either to a user or to a group. It has three attributes: user, group and special. The former two are foreign keys to django.contrib.auth.models.User and django.contrib.auth.models.Group. Either user, or group, or special, must be not null; the other two must be null. If user is not null, the Lentity represents a user. If group is not null, the Lentity represents a group. special, besides null, can have one of the following values:


These values denote any user (including the anonymous user), and any logged on user.


This value denotes the owner of the entry. This is useful in cases where we need to generalize the owner; for example, in state-transition rules, such as “the owner of an object has permission to publish it”.


These values indicate the set of users who have the respective permission. This can also used in state-transitions, for example: “users who have edit permission may publish”. Care should be taken to avoid circles, such as “a user with edit permission may edit”, so EntryPermission and StatePermission should generally not refer to such special users.

There is the following method:

includes(user, entry=None)

Return True if the lentity represents user or a group that contains user. user is a django.contrib.auth.models.User instance. entry should also be supplied, in case the lentity is OWNER, PERM_VIEW, PERM_EDIT, PERM_DELETE, PERM_ADMIN, or PERM_SEARCH; in these cases, whether user is a member of the lentity depends on the entry.

class zadig.core.models.State

A list of states. Contains only a descr attribute.

class zadig.core.models.StatePermission

A state is a collection of permissions. This model stores the permissions that comprise the state. It has three attributes, state, lentity and permission, which are foreign keys to State, Lentity and Permission, and designate that said lentity has said permission on said state.

class zadig.core.models.StateTransition

A state transition. Has three attributes, source_state and target_state (both foreign keys to State), and lentity (foreign key to Lentity, the user or, more commonly, group who has permission to perform this transition).

class zadig.core.models.Workflow

A workflow is a collection of states and state-transition rules. The model has three attributes: name, which is a string, states, and state_transitions. The last two are many to many fields to State and StateTransition.

class zadig.core.models.VObjectMetatags

Stores the metatags of a vobject. Has five attributes: vobject and language are foreign keys to the respective models; title, short_title and description are strings. Also has method get_short_title(), which returns the short title, or the title if the short title is empty. The related name is metatags.

The default manager has an additional property, default, which returns an appropriate set of metatags. Normally the manager has access to the request object, in which case it returns the metatags in the effective language. Failing that (because the request object is unavailable, or because there are no metatags in the effective language), it returns the metatags in the language of the vobject; and if such a set does not exist, it returns a random set of metatags.

class zadig.core.models.ContentFormat

A lookup storing content formats, such as “text/html” or “text/x-rst”. Has only a descr field.

class zadig.core.models.MultilingualGroup

Stores the multilingual groups. It has no field besides id, as it is only used to group multilingual entries together through their multilingual_group field. It has a check() method, which checks for integrity: it deletes the group if it contains less than two entries, checks that there are no multiple language occurrences in the group, makes other similar checks, and raises ValidationError if a check fails.

class zadig.core.models.Page(VObject)

Inherits VObject and designates a page. Has attributes format, a foreign key to ContentFormat, and content, a text field.

class zadig.core.models.File(VObject)

Inherits VObject and designates a file. Has attribute content, a file field.

class zadig.core.models.Image(VObject)

Inherits VObject and designates an image. Has attribute content, an image field.

Utility functions

The functions below are in module zadig.core.utils.


Return a list with the path items. Roughly path.split('/') but not confused by trailing slash, will ignore a starting slash, will work on an empty string or single slash, and it always includes an empty string as the first path item, which stands for the root entry.


Return a string with the path items joined into a path. path_items is a sequence of strings (supplied either as string arguments, or as a single sequence argument); each string may contain slashes. Roughly '/'.join(path_items) but will not result in duplicate slashes (strips leading and trailing slashes from each path item before joining), and the result never includes a trailing or leading slash.


Return the path to the current entry. This is something like request.path, but does not include any directive like __edit__ and so on.


Return a queryset of Lentity objects that either are or contain user, which is a django.contrib.auth.user object.


For information about these functions, see the request object.


Sanitize the HTML provided and return a sanitized version. This is done for two reasons: (a) to avoid cross site scripting attacks; (b) to discourage users from using too much markup. It only allows specific (whitelisted) tags and specific (whitelisted) attributes, deleting all the rest, and it also sanitizes the content of href and src attributes, by allowing only specific (whitelisted) URL schemes. The whitelists are hardwired in this version.



This is similar to the Django require_POST decorator, but it can be used to decorate any function or method and not only the views.