The database

Main principles

The Enhydris database is implemented in PostgreSQL. While the implementation of the database is through Django’s object-relational mapper, which is more or less RDBMS-independent, Enhydris uses PostgreSQL’s geographic features, so it is not portable. It also uses some custom PostgreSQL code for storing timeseries (however this is likely to change in the future).

In Django parlance, a model is a type of entity, which usually maps to a single database table. Therefore, in Django, we usually talk of models rather than of database tables, and we design models, which is close to a conceptual database design, leaving it to Django’s object-relational mapper to translate to the physical. In this text, we also speak more of models than of tables. Since a model is a Python class, we describe it as a Python class rather than as a relational database table. If, however, you feel more comfortable with tables, you can generally read the text understanding that a model is a table.

If you are interested in the physical structure of the database, you need to know the model translation rules, which are quite simple:

  • The name of the table is the lower case name of the model, with a prefix. The prefix for the core of the database is hcore_. (More on the prefix below).
  • Tables normally have an implicit integer id field, which is the primary key of the table.
  • Table fields have the same name as model attributes, except for foreign keys.
  • Foreign keys have the name of the model attribute suffixed with _id.
  • When using multi-table inheritance, the primary key of the child table is also a foreign key to the id field of the parent table. The name of the database column for the key of the child table is the lower cased parent model name suffixed with _ptr_id.

There are two drawings that accompany this text: the drawing for the conceptual data model, and the drawing for the physical data model. You should avoid looking at the physical data model; it is cluttered and confusing, since it is machine-generated. It is only provided for the benefit of those who are not comfortable with Django’s object-relational mapping. However, it is best to learn to read the conceptual data model; if you become acquainted with the Django’s object-relational mapping rules listed above, you will be able to write SQL commands effortlessly, by using these rules in your head. The drawing of the physical data model is also far more likely to contain errors or to be outdated than the drawing and documentation for the conceptual data model.

The core of the Enhydris database is a list of measuring stations, with additional information such as instruments, photos, videos, and so on, and the hydrological and meteorological time series stored for each measuring station. This can be used in or assisted by many more applications, which may or may not be needed in each setup. A billing system is needed for agencies that charge for their data, but not for those who offer them freely or only internally. Some organisations may need to develop additional software for managing aqueducts, and some may not. Therefore, the core is kept as simple as possible. The core database tables use the hcore_ prefix. Other applications use another prefix. The name of a table is the lowercased model name preceeded by the prefix. For example, the table that corresponds to the Gentity model is hcore_gentity.


Originally, the database was designed in order to be multilingual, that is, so that the content could be stored in an unlimited number of languages. The django-multilingual framework was used for this purpose. However, django-multilingual bugs slowed development too much, and it was decided to go for a more modest solution: texts are simply stored in two languages: the local language and the alternative language. For example, for a description, there are the “descr” field and the “descr_alt” field. Which languages are “descr” and “descr_alt” depends on the installation. For example, we use Greek as the local language and English as the alternative language.

We hope to get rid of this, but this will involve fixing django-multilingual or using another multilingual framework.

When any field in the API is marked as being multilingual, it means that it is accompanied by an additional identical field that has “_alt” appended to its name. (It also means that, instead, it should be defined in a Translation class nested in the model class, as would be the case if django-multilingual were used.)

Lookup tables

Lookup tables are those that are used for enumerated values. For example, the list of variables is a lookup table. Most lookup tables in the Enhydris database have three fields: id, descr, and short_descr, and they all inherit the following abstract base class:

class enhydris.hcore.models.Lookup

This class contains the common attribute of the lookup tables:


A multilingual character field with a descriptive name.

Most lookup tables are described in a relevant section of this document, where their description fits better; for example, StationType is described at Section Station and its related models.


The Lentity is the superclass of people and groups. For example, a measuring station can belong either to an organisation or an individual. Lawyers use the word “entity” to refer to individuals and organisations together, but this would create confusion because of the more generic meaning of “entity” in computing; therefore, we use “lentity”, which is something like a legal entity. The lentity hierarchy is implemented by using Django’s multi-table inheritance.

class enhydris.hcore.models.Lentity

A multilingual text field of unlimited length.

class enhydris.hcore.models.Person

The above four are all multilingual character fields. The initials contain the initials without the last name. For example, for Antonis Michael Christofides, initials would contain the value “A. M.”.

class enhydris.hcore.models.Organization

name and acronym are both multilingual character fields.

Gentity and its direct descendants: Gpoint, Gline, Garea

A Gentity is a geographical entity. Examples of gentities (short for geographical entities) are measuring stations, cities, boreholes and watersheds. A gentity can be a point (e.g. stations and boreholes), a surface (e.g. lakes and watersheds), a line (e.g. aqueducts), or a network (e.g. a river). The gentities implemented in the core are measuring stations and water basins. The gentity hierarchy is implemented by using Django’s multi-table inheritance.

class enhydris.hcore.models.Gentity

A multilingual field with the name of the gentity, such as the name of a measuring station. Up to 200 characters.


A multilingual field with a short name of the gentity. Up to 50 characters.


A multilingual field with general remarks about the gentity. Unlimited length.


The water basin where the gentity is.


The water division in which the gentity is. Foreign key to WaterDivision.


The country or other political division in which the gentity is. Foreign key to PoliticalDivision.

class enhydris.hcore.models.Gpoint(Gentity)

This is a GeoDjango PointField that stores the 2-d location of the point.


Specifies the reference system in which the user originally entered the co-ordinates of the point. Valid srid‘s are registered at See also


This boolean field has the value True if the horizontal co-ordinates are approximate. This normally means that the user who specified the co-ordinates did not really know the location of the point, but for convenience placed it somewhere visually so that the GIS system can have a rough idea of where to show it and e.g. in which basin it is.


These attributes store the altitude. asrid specifies the reference system, which defines how altitude is to be understood. asrid can be empty, in which case, altitude is given in metres above mean sea level.

class enhydris.hcore.models.Gline(Gentity)

The starting and ending points of the line; foreign keys to Gpoint.


The length of the line in meters.

class enhydris.hcore.models.Garea(Gentity)

The size of the area in square meters.

Additional information for generic gentities

This section describes models that provide additional information about gentities.

class enhydris.hcore.models.PoliticalDivision(Garea)

From an administrative point of view, the world is divided into countries. Each country is then divided into further divisions, which may be called states, districts, counties, provinces, prefectures, and so on, which may be further subdivided. Greece, for example, is divided in districts, which are subdivided in prefectures. How these divisions and subdivisions are named, and the way and depth of subdividing, differs from country to country.

PoliticalDivision is a recursive model that represents such political divisions. The top-level political division is a country, and lower levels differ from country to country.


For top-level political divisions, that is, countries, this attribute is null; otherwise, it points to the containing political division.


For top-level political divisions, that is, countries, this is the two-character ISO 3166 country code. For lower level political divisions, it can be a country-specific division code; for example, for US states, it can be the two-character state code. Up to five characters.

class enhydris.hcore.models.WaterDivision(Garea)

A water division is a collection of basins. Water divisions may be used for administrative purposes, each water division being under the authority of one organisation or organisational division. Usually a water division consists of adjacent basins or of nearby islands or both.

class enhydris.hcore.models.WaterBasin(Garea)

A water basin.


If this is a subbasin, this field points to the containing water basin.


The water district in which the water basin is.

class enhydris.hcore.models.GentityAltCodeType(Lookup)

The different kinds of codes that a gentity may have; see GentityAltCode for more information.

class enhydris.hcore.models.GentityAltCode

While each gentity is automatically given an id by the system, some stations may also have alternative codes. For example, in Greece, if a database contains a measuring station that is owned by a specific organisation, the station has the id given to it by the database, but in addition it may have a code assigned by the organisation; some also have a code created by older inter-organisational efforts to create a unique list of stations in Greece; and some also have a WMO code. This model therefore stores alternative codes.


A foreign key to Gentity.


The type of alternative code; one of those listed in GentityAltCodeType.


A character field with the actual code.

class enhydris.hcore.models.FileType(Lookup)

A lookup that contains one additional field:


The mime type, like image/jpeg.

class enhydris.hcore.models.GentityFile

This model stores general files for the gentity. For examples, for measuring stations, it can be photos, videos, sensor manuals, etc.


A multilingual short description or legend of the file.


Multilingual remarks of unlimited length.


For photos, it should be the date the photo was taken. For other kinds of files, it can be any kind of date.


The type of the file; a foreign key to FileType.


The actual content of the file; a Django FileField. Note that, for generality, images are also stored in this attribute, and therefore they don’t use an ImageField, which means that the few facilities that ImageField offers are not available.

class enhydris.hcore.models.EventType(Lookup)

Stores types of events.

class enhydris.hcore.models.GentityEvent

An event is something that happens during the lifetime of a gentity and needs to be recorded. For example, for measuring stations, events such as malfunctions, maintenance sessions, and extreme weather phenomena observations can be recorded and provide a kind of log.


The Gentity to which the event refers.


The date of the event.


The EventType.


The username of the user who entered the event to the database.


A report about the event; a text field of unlimited length.