Working with the database
DB interface reference
Tabbli provides a global DB
interface for access to the database. It is available inside all sites templates, automation scenarios and computed properties templates. You can use the next optional argument with it:
request
- use it to specify current request object, it is using by filtering component
If you create the object like DB(request)
you will be able to use a list of methods:
get_section(key, **options)
- returns aSectionInterface
object as an interface for a section with specifiedkey
. You can specify extra arguments for passing different options to the interface. In most cases you do not need to use this method.get_collection(key,**options)
- returns aCollectionInterface
object as an inteface for a collection with specifiedkey
. All collections keys are unique, so you do not actually useget_section
as part of the chain, useget_collection
directly.get_variable(name, default_value=None)
- returns a value of system variable with specifiedname
. Returnsdefault_value
if the variable does not exist.set_variable(name,value)
- set system variable with specified name tovalue
. If the variable does not exist it will be created.inc_variable(name, delta=1, start_value=0)
- increment a variable with specifiedname
fordelta
(1 by default). If the variable does not exist it will be created with avalue start_value+delta
. The variable must contain only integer value. In other case an error will be generated.dec_variable(name, delta=1, start_value=0)
- works likeinc_variable
but for decrement.
SectionInterface
supports the next methods:
get_collection(key,**options)
- returns aCollectionInterface
object as an inteface for a collection with specifiedkey
. Work the same asget_collection
method fromDB
.get_object()
- returns an internalSection
object. You can use it for some advanced use cases.
CollectionInterface
supports the next options:
records_per_page
(default value isNone
) - activates using pagination and limits a result for specified records count per page. Set it to integer value for enabling pagination. Or you can useNone
for disabling it (returns all records as a single page). Do not forget to use pagination if you result could contain more than 30 records (approximately). Tabbli also has internal limit for getting records on a single page which is not strict and calculates individually for different data sets.page
(default value is 1) - specify active page for paginator. Should be integer and more than 1.use_page_number_param
(default value is None) - using for passing GET parameter value with specified name from current page URL for overridepage
parameter. For example, if you URL looks like/results/?page=1
you can setuse_page_number_param='page'
for automatically init page option with number 1. It is a shortcut. In other case you need to use construction likepage=request.GET.get('page')|int(1)
for doing the same. Also if useuse_page_number_param
option system will do some basic error resolving (like not valid value for example).subset_key
- specify a subset record key for filter results.parent_key
- specify a record key if you want to query children records for some parent record.request
- the same option like inDB
object.
You can also use the next methods:
get_object()
- returns an internalCollection
object. It is not necessary for most of use cases.get_filtered_records(filter_data=None, use_pagination=True, ordering=None)
- returns a dictionary with filtered records and additional metadata. Usefilter_data
if you need filter records with specified filters. Iffilter_data=None
system uses GET parameters from current request. Useuse_pagination=False
for completely disable pagination feature. Do not use it for large data sets because it cause dramatically slow down your system. Use ordering attribute for specify ordering rules. By default, all records are ordering byname
property. If you have, for example, property with nameposition
and want to sort with it, you should useordering='+prop_position'
for it. If you have Choice property with namefloor
and want to sort records related to the choice options position, you should useordering='+sort_index_floor'
.get_first_filtered(**kwargs)
- works likeget_filtered_records
with the same attributes but returns only a first record orNone
if it does not exist.get_record_by_id(id)
- returns an internalRecord
object for a record with specifiedid
. Raises an error if the record does not exist.get_record(key, or404=False)
- returns an internalRecord
object for a record with specified key. Ifor404=True
force system to show "404 Not found" error page with proper status if record does not exist. By default, raises a server error.create_record(record_type_key=None, name=None, properties=None, parent_key=None, subset_keys=None)
- creates a new record in particular collection. Attributerecord_type_key
is using for collection with multiple record types support for specify which exact type of record you want to create. In most cases you should specify name attribute as a dictionary with language code as a key. For example,name={'en':'Sample record'}
. You may ignore this attribute if name is a computed property for current record type. In such case name will be generated automatically based on specified computation template. Record properties are using in the system internal format as a dictionary. So, please check information about internal representation for different property types for doing it right. If you want to create a child record for another record, you should specifyparent_key
attribute. Initsubset_keys
with a list of record keys if you are using subsets support.get_queryset()
- returns an internalQuerySet
object for working withRecord
objects on the low level. You should not use this method in most of the use cases.get_record_form(record_type_key=None, record=None, use_request_data='auto', languages=None, ignore_name=False, property_keys=None, widgets_override=None, prefix='')
- returns aRecordForm
object for rendering, validating and submitting a record form. If you are using collection with multiple record types support you should specify the exact type. For the purposes of editing existing record you should specify it in record attribute. It should be an object of typeRecord
(not its dictionary or json representation). Attribute use_request_data supports a few different values:True
if you want to use data from POST request for initializing and validation,False
if you do not need it and'auto'
(common case) if you need to do form validation if particular request method is POST, or not if GET. Attribute languages is using for limit multilingual fields to work only with particular languages in multilingual configurations. For example, if your system supports English and Dutch, but you want to show only English version of record name field you should uselanguages=['en']
. If name is a computed property you should useignore_name=True
for hide Name input field from the form. For managing other record properties visibility useproperty_keys
attribute. Specify a list of property keys you want to be included to the form. Some properties (different type of relations for example) support different alternate widgets. If you need to use a widget which is differ from one using in management console you can usewidgets_override
attribute. For example,widgets_override={'gender':'radio_select'}
. In cases, when need to show many copies of a one single form and work with them your should useprefix
attribute for differentiate data from different form instances.get_record_type(key)
- returns an internalRecordType
object with specified key.get_property(record_type_key, property_key)
- returns an internalRecordProperty
object with particular key for specified record type.get_property_option(record_type_key, property_key, option_key)
- returns an internalPropertyOption
object for specified property option.get_scenario(key)
- returns an internalScenario
object with specified key. You can use it for some advanced cases with automation.get_button(key)
- returns an internalActionButton
object with specified case. It is also could be used for some advanced use cases.
Simple queries with filtering via URL parameters
You can use DB
interface for querying records from the database. Parameters passed from URL can are using for filtering data. Look at the example. Here we have the collection with key students
and we want to get all records with 20 records per page. The number of active page is passing via GET parameter:
So, if we have a controller which is opening the template on URL /students/
we can go to /students/?page=1
(if we ignore page
parameter it will mean that page=1
). In such case, variable vacancies will contain the next data (it is an example):
As you see attribute records
contains a list of records. Also you have a list of additional attributes with meta information:
show_property_filters
-True
if the collection has filtered properties.search_query
- value of the&search=...
parameter.filtered
=True
if page URL contains filtering attributes orFalse
if not.total
- total number of filtered records.page
- current page.show_paginator
-True
if number of pages is more than one.paginator
- a list of objects with page numbers (maybe some extra info in future).last_page
- a number of last page.
As you can see we also have two filters, for properties Classes
and User
. Note, that get_filtered_records returns filters blocks not only for filtered properties but also for all indexed properties like relations ones.
If you try to change the URL to something like /students/?f-classes=class-french-at-the-evening&search=beth
the same template tag will return completely different result:
So, as you can see, we can use the same template for generating filtered list of records which support dynamic filters. See how we can show retrieved data on the page:
Simple queries with filtering via attributes
In many cases you need to query some records from the database with some strict filters set which cannot be modified via URL parameters. In such case you can use something like that:
We also can set how much records we want to get:
Another example should be useful when we have some collection which implements a some part of a user profile. In such case we often need get only first record from the result or None
. That is because in most cases we are using one-to-one relation. Look at the two different solutions:
Dictionary representation for a record
Filtering results contain a list of Record
objects. Such objects contain all properties values but not all information you can get directly. For example, if you read a record properties attribute you can see something like that:
Here you can see an internal representation of some types of properties. For example, dates are storing as strings in format YYYY-MM-DD
and many-to-many relations are using lists of related record keys. It is pretty compact form for storing data in the database but not convinient in most cases to use on site pages (or in other platform components when you want to access your data). Of course, you also can work with other attributes of Record
object, like name
, etc., but it is still pretty hard. For that purposes, you can use method .get_dict()
which returns much more complex structure:
As you can see, here "Date of birthday" property represents as a datetime
object and list of related classes contains information about related records names and ids. Sometimes you need to query some related records to the template. In such case you can use get_record
method for getting a record with particular record key:
Or you can use include_related
optional attribute for get_dict
method for prefetch additional data to the result. This attribute must be a list of collections name the record is related with. So if you use {% set s_dict=student.get_dict(include_related=['classes']) %}
the result will be like this:
As you see, here property classes
contains a full information about related records. Use this feature carefully because you increase your resouces usage in such case.
New records creation
Tabbli supports two kinds of solution for creation new records in the database:
With
create_record(...)
method.With a form's
save()
method.
If you need to just create new record in non-interactive mode better to use create_record
method. For example, here we are trying to create student profile if it is not available yet for a current user (we can put this code somewhere in the base template):
Or, if we want to implement an interface where user can create a some record (like Job record) with filling the form we can use the next code:
If the form contains all necessary data and you do not need to modify it before saving to the database, you don't need to use commit=False
attribute in the form save()
method. You can just use {% set job = form.save() %}
for write data directly to the database.
Last updated