Sqlwidget

This is the base class for SqlMask and SqlTable that implements the common interface.

mandatory argument

The first and mandatory argument is the mapper of the object that will be retrieved and displayed. Alternatively any other objects from which the sqlwidget infers them (e.g.: the tablename and the metadata where the table can be autoloaded):

  • a mapper
  • a mapped class
  • table name (requires you also pass metadata)

In older releases different options: class_, table, or mapper where used. Now a DeprecationWarning is raised if you use those opts.

main options

Any sqlwidget needs a session as well.

Metadata is also used when auto-loading tables referenced by foreign keys to display a better representation of the referenced record.

dbproxy

Since a typical scenario is to have to provide a session different in each SqlWidget and a metadata, an object is provided -dbproxy- that can be initialized from the engine specification:

from sqlkit.widgets import SqlMask, SqlTable
from sqlkit import DbProxy
db = DbProxy(engine="sqlite:///model/movies.sqlite")
SqlTable("movies", dbproxy=db)

below you can see some alternatives that would work as well:

Session = sessionmaker(bind=self.metadata.bind, autocommit=False)
sess = Session()
meta = MetaData()
meta.bind = "sqlite:///model/movies.sqlite"
#
SqlTable("movies", session=sess, metadata=meta)

# passing a mapped class (Movie here is build with declarative layer):
# the metadata is found from the mapper.local_table.metadata

SqlTable(Movies, session=sess)

Session are created with autoflush=False, expire_on_commit=False but can be changed when building DbProxy.

Since version 0.8.6 default value for autocommit has been turned to True to prevent idle in transaction in postgresql.

session & expire_on_commit

The reason to have expire_on_commit=False is that if you don’t set it, after every commit, you have to reload all objects and the interface turns very slow, especially when working with a remote database.

expire_on_commit is a recent addition to sqlalchemy session (around sa rel. 5.03rc) so I try it and fallback to default that would turn to be slower. Previous 0.5 rel called it autoflush

class sqlkit.widgets.common.sqlwidget.SqlWidget(see_below)

The main widget used to edit a table or sqlalchemy selectable, i.e. almost everything you can express as table or for which you can provide a mapper or a mapped class.

You won’t use this as such: this is just the parent of SqlTable an SqlMask.

__init__(what=None, mapper=None, layout=None, layout_nick='default', naked=False, geom=(-1, -1), rows=10, addto=None, gui_field_mapping=None, title=None, show=True, icons='', col_width=None, table=None, tables=None, class_=None, join=None, ro=False, noup=[], sql=None, constraint={}, dbproxy=None, order_by=None, limit=200, field_list=None, session=None, metadata=None, ignore_fields=None, hide_fields=None, single=False, nick=None, about_dialog=None, dev=False, xml=False, relationship_mode='SINGLE', relationship_path=None, relationship_leader=None, format=None, label_map=None, hooks=None, mode='biud')
Parameters:
  • what – this is the table/mapper/class to be displayed. You are encouraged to use it as first argument. The keyword ‘what’ is just here for backward compatibility as the first versiona had different keywork for table/classes/mappers
  • table

    the table to be edited. May be a string in which case it will be autoloaded or a sqlalchemy.Table object. When using table argument no info on relationship to other table are passed except those contained in the Foreignkey, so you won’t be able to show one2many or many2many relationships

    If you pass 2 table as in movie director sqlkit will try to join them and build a proper mapper.

    This keyword is now deprecated and will disappear in future releases. You are encouraged to use the table as first argument istead.

  • class_

    a sqlalchemy mapped class (eg: that defined via declarative layer). The mapper will be automatically found

    This keyword is now deprecated and will disappear in future releases. You are encouraged to use the class as first argument istead.

  • mapper

    a mapper. All fields will be desumed by introspection

    This keyword is now deprecated and will disappear in future releases. You are encouraged to use the mapper as first argument istead.

  • session – the session to be used
  • dbproxy – a dbproxy object that holds info on metadata and can create sessions
  • metadata – a metadata object needed for introspection if reflection is required
  • hooks – the Hooks class to be used for this widget
  • ro=False – if set to True the table is not updateable. Old way to set mode=’b’
  • mode – mode for the widget. See set_mode for more explanations
  • noup=[] – a list of field names that will not be allowed to update. The list may be also a string space or comma separated
  • order_by=None

    order by column to be used when selecting. Can be

    • a list of sqlalchemy order_by clause elements
    • a string as would be set in ordinary SQL statements
    • a string of fields as would be used in filters: user_id__username (note the double_underscore). If you also have a relation user that points to the same table you can use user__username with the same effect.
  • limit=200 – limit attribute to be used selecting
  • field_list=None – a field_list to be shown. This can also be set via “set_field_list”. Operate via toggling visibility of the columns in Tables or preventing autogenerated layout to show.
  • ignore_fields = None – field to be ignores (will be deleted from field list to field_list). Can be a list or a comma separated string.
  • hide_fields = None – Only valid for SqlTable. List of columns to be hidden (but can be interactively made to show up from menu). Can be a list or a comma separated string.
  • single=False – if a table is opened in single mode, destroying the table quits the gtk main loop.
  • format=format_dict – equivalent to using: .set_format(format_dict). See Localization for more info
  • col_width – a dictionary of field_name/width for columns (e.g: {‘zip_code’ : 6}). It can be passed to masks also that will handle on to nested tables. In this case if you have a relation with attribute ‘address’ you may use: {‘address.zip_code’: 6}
  • gui_field_mapping – a dict that maps field names to Field classes. You only need to set this for non persisted fields, i.e. field that are not already defined in the mapper
  • title – the title for the gtk.Window.
  • show – a boolean to tell if the Window will be visible
  • dev – if a sqlwidget is opened in dev mode and no field_list is passed, the primary key is shown even if it’s a serial, if it’s not in dev mode it will be hidden
  • naked=False – the table will be rendered without any buttons. It’s used in layout when you want to add the table/mask to another layout (eg: in relationships). Default: False.
  • addto=None – if set must be a container to which the sqlwidget will be added
  • about_dialog – Pass a dialog to invoke when info is called. A gtk.AboutDialog info will be created by default if no about_dialog is passed
  • label_map – argument: label_map dictionary as explained in Localization Adds a mapping between field_names and msgid/translation for gettext and tooltips
  • layout – string description of the layout as described in SqlMask. It’s used to generate the layout for SqlMask also for table’s RecordInMask.
  • layout_nick – nick name of a layout to be used. Defaults to default. A layout can be registered under a nick via register layout function
  • relationship_mode – SINGLE, m2m, m2o, o2m
  • relationship_path – the path needed to join two tables. See sqlalchemy on ‘quering with joins’. Used by filter_panel to setup join condition when filtering when relationship_mode is not SINGLE. May be a list or a string: ‘genres’, ‘director nationality’, [‘director’,’nationality’]
  • relationship_leader – the main sqlmask (sqltable) that “owns” the filter_panel
add_constraint(OR=False, **kwargs)

Add Constraints. A constraint may be expressed via keyworks in django-like syntax. Eg.:

name_like='uno%'
genres__name__regexp='sto'
director_id__birth_date__gte='1/1/1970'

if multiple conditions are passed, they will be ANDed unless ‘or_=True’

A constraint may also be build directly into the self.query object.

Parameter:OR – the condition will be ORed
add_filter(active=True, force_=True, **kw)
Parameter:active – boolean: make the filter active/inactive

see Filters and add_constraint above. Note that filter are always ANDed (OR arameter is not available).

If you have a field_name named ‘active’, the active parameter will hide it. Use keyword active__eq to bypass it.

resize(width, height)
Resize the window. It accepts also -1 as value in which case sets the current value, so it can be used to change just one dimention.
set_records(records=None, pk=None)

set ‘records’ as the list of record to manage

Parameters:
  • records – use ‘records’ as the list of records
  • pk – use primary key pk as te pk to show (TODO make it work with composite pks)
set_format(format_dict)

set_format(format_dict). format_dict is a dictionary whose key is the field_name and the value is the format of the column. Format_dict can have keys that do not correspond to any field in the widget. That makes it possible to reuse a format dict.

The format dict is passed to related widgets as well.

See Localization for more info

Parameter:format_dict – a dict of field_name/format strings
is_mask(obj=False)
Return True if the sqlwidget is a Sqlmask
is_table(obj=False)
Return True if the sqlwidget is a SqlTable
sb(text, seconds=10, delay=False)

Write on the status bar if present in this sqlwidget or in the relationship_leader

Adds a message in the stack of messages of the status bar, and removes it after seconds seconds. If delay=True it uses gobject.idle_add to give more chance to be visible (not hidden by other automatic messages)

Parameters:
  • text – the text to be written
  • seconds – how may seconds the text should stay visible
  • delay – boolean. If True message is added in an idle cicle. It means it will be shown after other possibly scheduled automatic messages that would hide its visibility.
get_value(field_name, shown=False)

return the value from the widget

Parameters:
  • field_name – the field_name
  • shown – boolean: in case field is a foreign key, True indicates we want the dislayed value rather than the real one
set_value(field_name, field_value, fkvalue=None, initial=False, shown=False)

set the value of any field present in gui_fields. Uses field.set_value if initial is False, run on_change_value

Parameters:
  • field_name – the field_name to be changed
  • field_value – the new value
  • fkvalue – a possible foreign key value. It’s here just for compatibility with SqlTable’s one
  • initial – a boolean indicating if it’s an initial value (passed to field)
  • shown – a boolean indicating if the value is the displayed value (passed to field)
get_current_obj()
Return the corrently edited obj. Note that in Table widgets, selection can already be elsewhere (as in on_selection_change).
set_mode(mode=None, reset=False, delay=False)
Parameters:
  • mode – the mode as explained below. If None, mode will be refreshed to last declared state (i.e.: reguardless of what you may have changed by hand acting on actiongroups).
  • reset – if True the mode will be completely reset. Needed to make the mode of a related table independent from the mode of the master
  • delay – if True, the mode is set but interface is not immediately updated

Set mode for this widget. Mode can be a string composed with the following letters that correspond to permissions possibly preceded by + or -.

s:SELECT. The user can view the records already selected (i.e. use Forward/Backward) or set by set_records. This is always granted and as such it’s pointless to set it (or revoke it)
i:INSERT. The user can insert new records
u:UPDATE. The user can update records
d:DELETE. The user can delete records
b:browse. The user can use the filter panel

If mode start with + or - the following permissions are granted/revoked for the widget by adding or removing from the modes already present. If no sign is used the mode is set.

mode is a property, you can set it directly: self.mode = 'b'

The mode influences permission by setting menu entries active or not. It’s not acting on the session. If an object has been inserted in the session a simple update operation can let it be inserted. This is by design.

Related table inheritate the same mode but you can programmatically reset it and make it independent from the master using option reset=True.

Implementation

Mode are implemented acting on uimanager/actionsgroup. You may read UiManager: menu and actions

Note

at present it’s not possible to insert a record in a table that is not updatable

add_temporary_item(item, menu, position=0, separator=False)

Adds an action to a menu and removes it with ‘selection-done’

Parameters:
  • item – the gtk.MenuItem that must be added
  • menu – the menu where the item needs to be added
  • position – the position where to insert the item (default: 0)
  • separator – boolean: add a separator (not implemented)
reload(limit=None, display=True, order_by=None, OR=False, **kwargs)

reload the data from the database taking all filter/constraints into consideration

Parameters:
  • limit – add a LIMIT clause (integer) to limit number of returned records. Permanent effect
  • order_by – reset order_by and apply when reloading. Permanent effect.
  • OR – (boolean, default False) as in add_constraint you can specify if conditions as in kwargs below should be ORed or ANDed (default)
  • kwargs – any filters accepted by the Django-like syntax and by add_filter

sql may be a tuple (sql_statement, bind_params) or just a sql_statement

commit(message='Salvato')

run session.commit() and take care of possible exceptions

Parameter:message – a message to be written in the status bar (default: Saved)
add_validation_error(error, field_name=None)

keep track of the error in self.validation_errors/validation_warnings so that a further process can collect them and present them to the user

Parameter:error – the ValidationError or an error message string
Field_name:the field_name to which the error refers. Defaults to ‘record validation’
add_not_null_error(field_name)

simple way to add a not nullable field error

Parameter:field_name – the field_name that cannot be nullable

Attributes

filter_panel
The FilterPanel widget

a container for all related sqlwidgets (i.e.: SqlWidgets that have a relation with this sqlwidget defined by sqlalchemy and that are displayed in the widget).

This is used in all situation in which you need to fine-tune the configuration of a related table (completion, layout, ...)

completions
a container of all completion objects. Needed to change the behavior of some completion (see Completion)
gui_fields
a container for all validation fields. A validation fields is an object that lives in sqlkit.fields and that knows how to represent a field and how to validate it.
gui_field_mapping
A dict used to force a map between a sqlkit.Field and a gui field. Read more in Fields.
query
the sqlalchemy query object (session.query(mapper)) with all constraints applied. Can be manipulated as necessary as long as it stay a query object.
layout

the layout definition for this SqlWidget. This is the used definition for SqlMask while for SqlTable is only used if the record is opened in SqlMask (right click on the record in SqlTable).

You can also set it on a related table:

GUI="director @ m2m=movies"
t = SqlMask(Director, layout=GUI, ...)
t.related.movies.layout = "title @ m2m=actors"
lay_obj
the sqlkit.layout.Layout object used to create the layout
current
the object represented by a mask or a possible selected record in a table or None. In SqlTable in the transient in which you are saving a record in a table (when selection changes) self.current will point to the obj that is to be saved (while the selected object may already be another one).
field_list
a list of the field names the GUI is handling. It comprises PropertyLoaders (i.e. properties of the class that act as a loader of other info - all relation are seen as PropertLoaders w/o column)
order_by
an order_by string or clause element. Same as parameter passed to the class. It’s a property, can be set in any moment.
session
the sqlalchemy session used for querying
defaults
an instance of sqlkit.db.defaults.Defauls instantiated with local=True as explained in Defaults local to the application. Any default set with this instance will only be visible in this sqlwidget.
title
the title of the Window.
mode
the mode describing permissions of the widget. See set_mode
noup

noup can be a set of field_name or a comma separated string with possible +- sign to add/remove field_names to the set of field_names that will not be possible to update

Note that to add a non editable field_name you must used ‘+field_name’. Using simply ‘field_name’ will reset the list to only that field_name

actiongroup_*
see UiManager: menu and actions
ui_manager
see UiManager: menu and actions
relationship_leader
Th case this sqlwidget is representing a relation of a SqlMask, that SqlMask is referred to as a relationship_leader.

Signals

pre-display:

A record is about to be displayed.

pre_display_cb(sqlwidget, obj):
Parameters:
  • sqlwidget – the widget that emitted the signal
  • obj – the object that is about to be displayed
record-selected:
 

A record has been displayed in Mask or selected in Table. The callback will just receive the widget as argument.

records_selected_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
record-saved:

A record has been saved. This signal is not emitted from within session extension. That means you are sure there will be just one signal for each button press on “save” button. This is issued from within the SqlWidget.commit() method independently from the fact that a real modification occurred, so you are not guaranteed any modification took place. Was originally added to implement a destroy of the widget when the save operation was performed.

The callback will just receive the widget as argument.

record_saved_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
record-new:

A new record has been added. The callback will just receive the widget as argument.

record_new_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
record-deleted:

A record has been deleted. The callback will receive the widget and the obj.

record_new_cb(sqlwidget, deleted_obj):
Parameters:
  • sqlwidget – the widget that emitted the signal
  • deleted_obj – the obje that was deleted

Note that this signal is emitted only for records deleted explicitly, i.e. records that where the current record in a mask/table. If a record is deleted as a side effect (e.g.: becouse cascade=”delete-orphan” is set) no signal is emitted for that obj.

records-displayed:
 

Records have been displayed. This may be after SqlWidget.reload() or SqlWidget.set_records(). The callback will just receive the widget as argument. See also context changed to see another signal that better tracks any change

records_displayed_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
after-flush:

flush has occurred, normally commit should not add any errors. This is implemented with a SessionExtension: if you used the default session obtained via get_session() you are assured that it will be correct. If you create a session by yourself, be sure to add sqlkit.db.proxy.SKSessionExtension to the session extensions or you won’t have this signal. Read more detailed explanation in hook on_after_flush

after_flush_callback(sqlwidget, object, session)
Parameters:
  • sqlwidget – the sqlwidget that emitted the signal
  • object – the object that was current when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in “dirty state”, “new” or “deleted”, but current was the one selected in a table or displayed in the main mask
  • session – the session that was flushed. The moment in which the signal is emitted you can still dig into session.dirty, session.new and session.deleted and find which attributes have been changed (you may want to use get_differences())
after-commit:

run from within after-commit SessionExtension. Callback signature is identical to after_flush_callback above.

delete-event:

emitted when the sqlwidget is destroyed

delete_event_callback(sqlwidget)
Parameter:sqlwidget – the sqlwidget that emitted the signal

Hooks

Beside signals there is another way to add controls: hooks. A hook is a function that will be called in particular moments only if present.

Hooks are the main way to customize the behavior of a sqlwidget. Some of the hooks (on_validation.*) are related to validation other are related to configuration (on init), others (on_activate) may be used to save typing.

Hooks are searched for in the methods of the instance of a class declared in the optional hooks argument or in the global registered hooks (see below).

Hooks can be registered using sqlkit.db.utils.register_hook() (and get_hook from utils module) so that any sqlwidget built on that table will use those hooks (unless the table is part of a join or another selectable!!). The advantage is that browsing data (e.g. using right click on a table row) can lead to opening tables that are not configured: registering hooks is a way to enforce configuration (and possibly constraints) on any widget.

As layout hooks can be registered with a nick (default is default) so that you can register different hooks for different editing flavors. E.g/: you can have a table for people and you can decide to open it with customer or provider layout/hooks just registering both layout and hooks and using argument layout_nick.

Use it with care as it may lead in situation in which not all fields are present (due e.g. to a different layout or different field_list)

The following hooks are defined:

on_change_value__field_name:
 

this hook is used to trace changes in the widget. It’s mainly meant to be used interactively but it is also triggered when set_value is invoked with initial=False. It’s not changed if the value is set on record change.

Currently it behaves differently for Table widgets or Mask widgets.

Mask widgets invoke it each meaningful changes i.e. each char for varchar/int field, each time a value is choosen for enum/foreign key fields, when a date is selected or validated for Date widgets.

Table widgets invoke this hook only when the field is leaved or activated (Return is pressed).

Note

Implementation of this hooks is currently limited to some fields: Varchar, Int, Float, Numeric, Enum, ForeignKey, Date, DateTime, Bool. Text, Images, Time and Interval are not implemented. Example 63c is a complete example that shows it.

on_change_value__field_name(sqlwidget, field_name, value,
fkvalue, field)
Parameters:
  • sqlwidget – the sqlwidget (SqlMask or SqlTable) that runs the hook
  • field_name – the field_name
  • value – the new value
  • fkvalue – if field is a ForeignKey or enum, the displayed value
  • field – the field
on_completion__field_name:
 

called when a completion is chosen

on_completion__field_name(sqlwidget, field_name, obj)
Parameters:
  • sqlwidget – the sqlwidget (SqlMask or SqlTable) that runs the hook
  • field_name – the field_name
  • obj

    the matched object in the completion. This obj has attributes for each field_name named in attrs attribute of the completion. You can add field_names to that attribute if you need them in this hook function:

    sqlwidget.completions.director_id.attrs += ['nation']
    

    allows you to add a completion hook:

    def on_completion__director_id(self, sqlwidget, field_name, obj):
        print obj.nation
        sqlwidget.set_value('nation', obj.nation)
    

    you can also reach field attributes as dict values: obj[‘nation’].

save_as:

New in version 0.9.3.

this hook is invoked each time a record is saved as a duplicate of another one. After the new record is filled and before it’s saved you can customize at your will. Callback function:

on_save_as(sqlwidget, old, new):
Parameters:
  • sqlwidget – the mask/table that invoked the hook
  • old – the old object that was copied
  • new – the new object

When this hook is present, no warning on how the copy is handled is raised as it’s considered that the programmer has already coped with all the tricky issues.

on_validation:

called when all the values have been collected in the object, before calling validation on all fields and related widgets. That’s a good point to implement any procedure to add automatism’s. Within this hook you can propagate errors to the validation machinery in two of ways:

  1. raising sqlkit.exc.ValidationError. Simple when just one error is found
  2. filling self.validation_errors: a dict holding all errors. The key is the field_name or “record_validation”, the value a list of error messages
on_validation(sqlwidget)
Parameter:sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook
on_field_validation__field_name:
 

called to validate a single field as for the previous on_validation. This is called from within the field's validation method

  1. raising sqlkit.exc.ValidationError. Simple when just one error is found
  2. filling self.validation_errors: a dict holding all errors. The key is the field_name or “record_validation”, the value a list of error messages
on_validation(sqlwidget, field_name, field_value, field)
on_activate__field_name:
 

when Return is pressed in Mask or Table. Good to complete fields via calculation on other fields (e.g.: total, vat...). The name derive from the GTK name ‘activate’ that is when you press ‘Return’ in an entry, even thought in a Table’s treeview it’s really connected to the cell’s edited signal (limited to Varchar and Numeric columns).

on_activate__field_name(sqlwidget, field_name, field)
Parameters:
  • sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook
  • field_name – the field_name
  • field – the sqlkit.widgets.common.field
on_init:

run as the last command of __init__. It’s main purpose is to allow to configure a widget in a way that will be handed over to a possible SqlMask generated right-clicking from a table row (see RecordInMask).

on_init(sqlwidget)
Parameter:sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook
on_pre_layout:

run before the layout is setup. It’s main purpose is to allow to add fields in gui_field_mapping (that is only useful for SqlMask). Note that you can force a field for a table’s attribute if you want setting info’s field key pointing to that field’s class.

on_init(sqlwidget)
Parameter:sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook

Note

Hooks invoked within the session extensions

The hooks that are called from within the session extensions, can be called several times if there are more sqlwigets sharing the same session (that happens for example each time you open a mask to edit the row of a table i.e.: RecordInMask)

on_after_flush:

run as the signal with the same name from within after_flush session extension method.

This hook is completely similar to after-flush signal, but is meant to be defined in a separate class so that it’s easier to propagate validation hooks to a spawned child (i.e.: RecordInMask, when opening a mask by right clicking on a table record). You’ll see that on_after_flush is called eather.

From the sqlalchemy documentation: “Note that the session’s state is still in pre-flush, i.e. new, dirty, and deleted lists still show pre-flush state as well as the history settings on instance attributes”. This is true for after_commit hook as well, it is not true for after_flush_postexec, that on the other had has already setup relation.

I end up in some circumstances to split the callback in two phases: one that detects if an action is needed from within the after_flush/after_commit phase, the second (may be a mail, or any other action) from within the hook on_record_saved, so that I can use the relations. Beware that you may have one call to on_after_flush and more different calls to on_after_commit

on_after_flush_callback(sqlwidget, object, session)
Parameters:
  • sqlwidget – the sqlwidget that emitted the signal
  • object – the object that was current when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in “dirty state”, “new” or “deleted”, but current was the one selected in a table or displayed in the main mask
  • session – the session that was flushed. The moment in which the signal is emitted you can still dig into session.dirty, session.new and session.deleted and find which attributes have been changed (you may want to use get_differences), but you won’t have correctly setup relation’s object
on_after_flush_postexec:
 

run as the signal with the same name from within after_flush_postexec session extension method.

As in the precedent hook this is called exactly within the session extension method by the same name. When it’s run the session will have no longer information on session.dirty/session.new/session.delete but will have all relations set-up.

The callback has the same signature as for hook on_after_flush

on_after_commit:
 

run after commit after_commit session extension method

The callback has the same signature as for hook on_after_flush

on_record_saved:
 
run from within the commit method of the widget, that

assures that will be issued just once. It’s just equivalent to the signal with the callback has the same signature:

record_saved_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal

Registered hooks and SessionEtensions

When hooks are registered their customizations are enforced each time the model for which they’re registered is called. That adds some complications you should be sure to understand if using one of these hooks:

  • on_after_flush
  • on_after_commit
  • on_after_flush_postexec

These hooks are called within a SessionExtension that calls hooks on any sqlwidget that may be using the same session. An m2m, m2o relation table share the same session as the Mask that holds them so that it’s pretty normal to have several different tables within the same session.

From the SessionExtension hooks are searched for in any of these so that you should write the hooks keeping in mind it can be called from another sqlwiget’s commit.

As an example, suppose you have a Mask with the following layout:

first_name
last_name
m2m=genres   m2m=actors

suppose movies, genres and actors have registered on_after_commit hooks. They will all be called on any commit. Adding a genre object will trigger on_after_commit on the Actor’s table and vice verse.

get_differences

Module sqlkit.db.utils provides a simple function that yields all modified attributes of an object, along with their old and new values

get_differences(obj)
Parameters:
  • obj – the object to be inspected
  • session – the session the object belongs to
Return type:

field_name, old_value, new_value. Old value and new value are lists.

get_history(obj, field_name)
Parameters:
  • obj – the object to be inspected
  • field_name – the field_name
  • session – the session the object belongs to
Return type:

new_value, unchanged_value, old_value, . Old value and new value are lists. The order is different from get_differences as this is exactly what is returned from the SA function

Saving varchar and text fields

Text fields with empty values will be saved as NULL. To change this behavior you need to set blank=True on fields:

t.gui_fields[field_name].blank = True

UiManager: menu and actions

Menu entries are handled via standard gtk.UiManager interface. One interface is created for each toplevel Window and for each view in a SqlTable widget. You can see some examples in the demo (70-72).

Standard actions are divided into the following categories:

General (self.actiongroup_general)
     PendingDifferences
     Quit
     Go
     Modify
     Tools
     File
     Help
     About
Table (self.actiongroup_table)
     HideColumns
     ShowColumns
     Records
     MaskViewFKey
     Export
     MaskView
     Zoom-fit
Insert (self.actiongroup_insert)
     New
     Save-as (just for SqlMask)
Delete (self.actiongroup_delete)
     Delete
     RecordDelete
Update (self.actiongroup_update)
     Save
     Undo  (just for SqlMask)
Browse (self.actiongroup_browse)
     Filters
     Reload
Select (self.actiongroup_select)
     Back
     Forward
Print (self.actiongroup_print)
     Print
Debug (self.actiongroup_debug)
     Gtk-tree

While for each table’s view you have:

Table
     HideColumns
     ShowColumns
     Records
     MaskViewFKey
     Export
     MaskView
     Zoom-fit
Insert
     New
Delete
     Delete
     RecordDelete

Changing an entry

To change an entry you can:

  1. add an actiongroup in which you have defined an action with the same name
  2. insert this actiongroup before it’s relevant one (position 0 is normally a good choice

Example #72 in the demo shows how to do it

Adding an entry

the standard way is shown in demo snippet #70:

  1. create an xml definition and add it to ui_manager
  2. create an action and add it to an actiongroup

You can also use a SqlTable method add_temporary_item that will add a temporary entry, so that it can be contextually changed. This way is demonstrated in demo snippet #71