@@ -1009,10 +1009,10 @@ def requery(
1009
1009
self .rows = rows
1010
1010
1011
1011
if len (self .rows .index ):
1012
- # if "sort_order" not in self.rows.attrs:
1013
- # # Store the sort order as a dictionary in the attrs of the DataFrame
1014
- # sort_order = self.rows[self.pk_column].to_list()
1015
- # self.rows.attrs["sort_order"] = {self.pk_column: sort_order}
1012
+ # # if "sort_order" not in self.rows.attrs:
1013
+ # # Store the sort order as a dictionary in the attrs of the DataFrame
1014
+ # sort_order = self.rows[self.pk_column].to_list()
1015
+ # self.rows.attrs["sort_order"] = {self.pk_column: sort_order}
1016
1016
1017
1017
# now we can restore the sort order
1018
1018
self .load_sort_settings (sort_settings )
@@ -1376,11 +1376,11 @@ def set_by_pk(
1376
1376
omit_elements = []
1377
1377
# don't update self/dependents if we are going to below anyway
1378
1378
self .prompt_save (update_elements = False )
1379
-
1380
- self .current_index = self .rows .index [self .rows [self .pk_column ] == pk ].tolist ()[
1381
- 0
1382
- ]
1383
1379
1380
+ # find current index of pk in resorted rows (not in-place)
1381
+ self .current_index = (
1382
+ self .rows .sort_index ().index [self .rows [self .pk_column ] == pk ].tolist ()[0 ]
1383
+ )
1384
1384
if update_elements :
1385
1385
self .frm .update_elements (self .table , omit_elements = omit_elements )
1386
1386
if requery_dependents :
@@ -1549,7 +1549,8 @@ def insert_record(
1549
1549
# Update the pk to match the expected pk the driver would generate on insert.
1550
1550
new_values [self .pk_column ] = self .driver .next_pk (self .table , self .pk_column )
1551
1551
1552
- # Insert the new values using ResultSet.insert_row(), marking the new row as virtual
1552
+ # Insert the new values using ResultSet.insert_row(),
1553
+ # marking the new row as virtual
1553
1554
self .insert_row (new_values )
1554
1555
1555
1556
# and move to the new record
@@ -1704,8 +1705,8 @@ def save_record(
1704
1705
# resultset if possible. The expected pk may have changed from autoincrement
1705
1706
# and/or concurrent access.
1706
1707
pk = (
1707
- result .lastrowid
1708
- if result .lastrowid is not None
1708
+ result .attrs [ " lastrowid" ]
1709
+ if result .attrs [ " lastrowid" ] is not None
1709
1710
else self .get_current_pk ()
1710
1711
)
1711
1712
current_row [self .pk_column ] = pk
@@ -1722,7 +1723,7 @@ def save_record(
1722
1723
1723
1724
# Lets refresh our data
1724
1725
if self .row_is_virtual ():
1725
- # Requery so that the new row honors the order clause
1726
+ # Requery so that the new row honors the order clause
1726
1727
self .requery (select_first = False , update_elements = False )
1727
1728
if update_elements :
1728
1729
# Then move to the record
@@ -1745,6 +1746,9 @@ def save_record(
1745
1746
# do not commit or rollback
1746
1747
self .driver .commit ()
1747
1748
1749
+ # Sort so the saved row honors the current order.
1750
+ self .sort (self .table )
1751
+
1748
1752
if update_elements :
1749
1753
self .frm .update_elements (self .key )
1750
1754
logger .debug ("Record Saved!" )
@@ -2187,7 +2191,6 @@ def purge_virtual(self) -> None:
2187
2191
"""
2188
2192
# remove the rows where virtual is True in place, along with the corresponding
2189
2193
# virtual attribute
2190
- # remove the rows where virtual is True in place, along with the corresponding virtual attribute
2191
2194
idx_to_remove = [idx for idx , v in self .rows .attrs ["virtual" ].items () if v ]
2192
2195
self .rows .drop (index = idx_to_remove , inplace = True )
2193
2196
for idx in idx_to_remove :
@@ -2214,13 +2217,14 @@ def sort_by_column(self, column: str, table: str, reverse=False) -> None:
2214
2217
rels = Relationship .get_relationships (table )
2215
2218
for rel in rels :
2216
2219
if column == rel .fk_column :
2217
- rows = rel .frm [
2218
- rel .parent_table
2219
- ] # change the rows used for sort criteria
2220
- target_col = rel .pk_column # change our target column to look in
2221
- target_val = rel .frm [
2222
- rel .parent_table
2223
- ].description_column # and return the value in this column
2220
+ # change the rows used for sort criteria
2221
+ rows = rel .frm [rel .parent_table ]
2222
+
2223
+ # change our target column to look in
2224
+ target_col = rel .pk_column
2225
+
2226
+ # and return the value in this column
2227
+ target_val = rel .frm [rel .parent_table ].description_column
2224
2228
break
2225
2229
2226
2230
print ("rels" , rels , "\n " )
@@ -2351,17 +2355,19 @@ def insert_row(self, row: dict, idx: int = None) -> None:
2351
2355
row_series = pd .Series (row )
2352
2356
attrs = self .rows .attrs .copy ()
2353
2357
self .rows = pd .concat ([self .rows , row_series .to_frame ().T ], ignore_index = True )
2354
- self .rows .attrs = attrs .copy ()
2355
- # not quite working, but closer
2356
- # just need to update attrs with new index
2357
- return
2358
+ self .rows .attrs = attrs
2359
+
2360
+ # I don't have the idx parameter working yet
2358
2361
if idx is None :
2359
2362
idx = len (self .rows .index )
2360
- idx_label = self .rows .index .max () + 1 if len (self .rows .index ) > 0 else 0
2361
- self .rows .loc [idx_label ] = row_series
2362
- self .rows .attrs ["sort_order" ][self .pk_column ].append (row [self .pk_column ])
2363
- self .rows_attrs ["virtual" ].loc [idx_label ] = True
2364
- self .rows .sort_index ()
2363
+ idx_label = self .rows .index .max () if len (self .rows .index ) > 0 else 0
2364
+ # self.rows.loc[idx_label] = row_series
2365
+ # self.rows.attrs["sort_order"][self.pk_column].append(row[self.pk_column])
2366
+ self .rows .attrs ["virtual" ].loc [idx_label ] = True
2367
+
2368
+
2369
+ # self.rows.sort_index() (this wasn't doing anything,
2370
+ # since it defaults to inplace=False)
2365
2371
2366
2372
2367
2373
class Form :
@@ -3310,7 +3316,7 @@ def update_elements(
3310
3316
elif ":table_insert" in m ["event" ]:
3311
3317
parent = Relationship .get_parent (data_key )
3312
3318
if parent is not None :
3313
- disable = (
3319
+ disable = bool (
3314
3320
len (self [parent ].rows .index ) == 0
3315
3321
or self ._edit_protect
3316
3322
or Relationship .parent_virtual (data_key , self )
@@ -5289,18 +5295,10 @@ def __init__(
5289
5295
self .table_heading : TableHeadings = table_heading
5290
5296
5291
5297
def __call__ (self , column ):
5292
- # store the pk:
5293
- pk = self .frm [self .data_key ].get_current_pk ()
5294
5298
if len (self .frm [self .data_key ].rows .index ):
5299
+ # sort_cycle takes care of storing pk and calling set_by_pk()
5295
5300
sort_order = self .frm [self .data_key ].sort_cycle (column , self .data_key )
5296
- # We only need to update the selectors not all elements,
5297
- # so first set by the primary key, then update_selectors()
5298
- self .frm [self .data_key ].set_by_pk (
5299
- pk ,
5300
- update_elements = False ,
5301
- requery_dependents = False ,
5302
- skip_prompt_save = True ,
5303
- )
5301
+
5304
5302
self .frm .update_selectors (self .data_key )
5305
5303
self .table_heading .update_headings (self .element , column , sort_order )
5306
5304
@@ -6076,239 +6074,6 @@ def set(
6076
6074
return df
6077
6075
6078
6076
6079
- class ResultSet2 (pd .DataFrame ):
6080
- """
6081
- The ResultSet class is a generic result class so that working with the resultset of
6082
- the different supported databases behave in a consistent manner. A `ResultSet` is a
6083
- Pandas dataframe with some extra functionality to make working with abstracted
6084
- database drivers easier.
6085
-
6086
- ResultSets can be thought up as rows of information and are build from a Pandas
6087
- DataFrame. Iterating through a ResultSet
6088
- is very simple:
6089
- ResultSet = driver.execute('SELECT * FROM Journal;')
6090
- for index, row in rows.iteritems():
6091
- print(row['title'])
6092
-
6093
- Note: The lastrowid is set by the caller, but by pysimplesql convention, the
6094
- lastrowid should only be set after and INSERT statement is executed.
6095
- """
6096
-
6097
- SORT_NONE = 0
6098
- SORT_ASC = 1
6099
- SORT_DESC = 2
6100
-
6101
- def __init__ (
6102
- self ,
6103
- rows : List [Dict [str , Any ]] = [],
6104
- lastrowid : int = None ,
6105
- exception : str = None ,
6106
- column_info : ColumnInfo = None ,
6107
- ) -> None :
6108
- """
6109
- Create a new ResultSet instance.
6110
-
6111
- :param rows: a list of dicts representing a row of data, with each key being a
6112
- column name
6113
- :param lastrowid: The primary key of an inserted item.
6114
- :param exception: If an exception was encountered during the query, it will be
6115
- passed along here
6116
- :column_info: a `ColumnInfo` object can be supplied so that column information
6117
- can be accessed
6118
- """
6119
- super ().__init__ (rows ) # initialize the DataFrame with the row values
6120
- self .lastrowid = lastrowid
6121
- self .exception = exception
6122
- self .column_info = column_info
6123
- self .sort_column = None
6124
- self .sort_reverse = False
6125
- self .attrs ["original_index" ] = self .index .copy () # Store the original index
6126
- self .attrs ["virtual" ] = pd .Series (
6127
- [False ] * len (self ), index = self .index , dtype = bool
6128
- ) # Store virtual flags for each row
6129
-
6130
- def __str__ (self ):
6131
- return str (self .to_dict (orient = "records" ))
6132
-
6133
- def fetchone (self ) -> pd .Series :
6134
- """
6135
- Fetch the first record in the ResultSet.
6136
-
6137
- :returns: A `pd.Series` object representing the row
6138
- """
6139
- return self .iloc [0 ] if len (self ) else pd .Series (dtype = object )
6140
-
6141
- def fetchall (self ) -> ResultSet :
6142
- """
6143
- ResultSets don't actually support a fetchall(), since the rows are already
6144
- returned. This is more of a comfort method that does nothing, for those that are
6145
- used to calling fetchall().
6146
-
6147
- :returns: The same ResultSet that called fetchall()
6148
- """
6149
- return self
6150
-
6151
- def insert_row (self , row : dict , idx : int = None ) -> None :
6152
- """
6153
- Insert a new virtual row into the `ResultSet`. Virtual rows are ones that exist
6154
- in memory, but not in the database. When a save action is performed, virtual
6155
- rows will be added into the database.
6156
-
6157
- :param row: A dict representation of a row of data
6158
- :param idx: The index where the row should be inserted (default to last index)
6159
- :returns: None
6160
- """
6161
- row_series = pd .Series (row )
6162
- if idx is None :
6163
- idx = len (self .index )
6164
- idx_label = self .index .max () + 1 if len (self ) > 0 else 0
6165
- self .loc [idx_label ] = row_series
6166
- self .attrs ["original_index" ] = self .attrs ["original_index" ].insert (
6167
- idx , idx_label
6168
- )
6169
- self .attrs ["virtual" ].loc [idx_label ] = True
6170
- self .sort_index ()
6171
-
6172
- def purge_virtual (self ) -> None :
6173
- """
6174
- Purge virtual rows from the DataFrame.
6175
-
6176
- :returns: None
6177
- """
6178
- virtual_rows = self .attrs ["virtual" ][self .attrs ["virtual" ]].index
6179
- self .drop (virtual_rows , inplace = True )
6180
- self .attrs ["original_index" ] = self .attrs ["original_index" ].drop (virtual_rows )
6181
- self .attrs ["virtual" ] = self .attrs ["virtual" ].drop (virtual_rows )
6182
-
6183
- def sort_by_column (self , column : str , table : str , reverse = False ) -> None :
6184
- """
6185
- Sort the `ResultSet` by column. Using the mapped relationships of the database,
6186
- foreign keys will automatically sort based on the parent table's description
6187
- column, rather than the foreign key number.
6188
-
6189
- :param column: The name of the column to sort the `ResultSet` by
6190
- :param table: The name of the table the column belongs to
6191
- :param reverse: Reverse the sort; False = ASC, True = DESC
6192
- :returns: None
6193
- """
6194
- # Target sorting by this ResultSet
6195
- rows = self # search criteria is based on rows
6196
- target_col = column # Looking in rows for this column
6197
- target_val = column # to be equal to the same column in self.rows
6198
-
6199
- # We don't want to sort by foreign keys directly - we want to sort by the
6200
- # description column of the foreign table that the foreign key references
6201
- rels = Relationship .get_relationships (table )
6202
- for rel in rels :
6203
- if column == rel .fk_column :
6204
- rows = rel .frm [
6205
- rel .parent_table
6206
- ] # change the rows used for sort criteria
6207
- target_col = rel .pk_column # change our target column to look in
6208
- target_val = rel .frm [
6209
- rel .parent_table
6210
- ].description_column # and return the value in this column
6211
- break
6212
-
6213
- def get_sort_key (row ):
6214
- try :
6215
- return next (
6216
- r [target_val ]
6217
- for _ , r in rows .iterrows ()
6218
- if r [target_col ] == row [column ]
6219
- )
6220
- except StopIteration :
6221
- return None
6222
-
6223
- try :
6224
- self .sort_values (
6225
- by = self .index , key = get_sort_key , ascending = not reverse , inplace = True
6226
- )
6227
- except KeyError :
6228
- logger .debug (f"ResultSet could not sort by column { column } . KeyError." )
6229
-
6230
- def sort_by_index (self , index : int , table : str , reverse = False ):
6231
- """
6232
- Sort the `ResultSet` by column index Using the mapped relationships of the
6233
- database, foreign keys will automatically sort based on the parent table's
6234
- description column, rather than the foreign key number.
6235
-
6236
- :param index: The index of the column to sort the `ResultSet` by
6237
- :param table: The name of the table the column belongs to
6238
- :param reverse: Reverse the sort; False = ASC, True = DESC
6239
- :returns: None
6240
- """
6241
- column = self .columns [index ]
6242
- self .sort_by_column (column , table , reverse )
6243
-
6244
- def store_sort_settings (self ) -> list :
6245
- """
6246
- Store the current sort settingg. Sort settings are just the sort column and
6247
- reverse setting. Sort order can be restored with
6248
- `ResultSet.load_sort_settings()`.
6249
-
6250
- :returns: A list containing the sort_column and the sort_reverse
6251
- """
6252
- return [self .sort_column , self .sort_reverse ]
6253
-
6254
- def load_sort_settings (self , sort_settings : list ) -> None :
6255
- """
6256
- Load a previously stored sort setting. Sort settings are just the sort columm
6257
- and reverse setting.
6258
-
6259
- :param sort_settings: A list as returned by `ResultSet.store_sort_settings()`
6260
- """
6261
- self .sort_column = sort_settings [0 ]
6262
- self .sort_reverse = sort_settings [1 ]
6263
-
6264
- def sort_reset (self ) -> None :
6265
- """
6266
- Reset the sort order to the original when this ResultSet was created. Each
6267
- ResultRow has the original order stored.
6268
-
6269
- :returns: None
6270
- """
6271
- self .sort_index (inplace = True )
6272
-
6273
- def sort (self , table : str ) -> None :
6274
- """
6275
- Sort according to the internal sort_column and sort_reverse variables. This is a
6276
- good way to re-sort without changing the sort_cycle.
6277
-
6278
- :param table: The table associated with this ResultSet. Passed along to
6279
- `ResultSet.sort_by_column()`
6280
- :returns: None
6281
- """
6282
- if self .sort_column is None :
6283
- self .sort_reset ()
6284
- else :
6285
- self .sort_by_column (self .sort_column , table , self .sort_reverse )
6286
-
6287
- def sort_cycle (self , column : str , table : str ) -> int :
6288
- """
6289
- Cycle between original sort order of the ResultSet, ASC by column, and DESC by
6290
- column with each call.
6291
-
6292
- :param column: The column name to cycle the sort on
6293
- :param table: The table that the column belongs to
6294
- :returns: A ResultSet sort constant; ResultSet.SORT_NONE, ResultSet.SORT_ASC, or
6295
- ResultSet.SORT_DESC
6296
- """
6297
- if column != self .sort_column :
6298
- self .sort_column = column
6299
- self .sort_reverse = False
6300
- self .sort (table )
6301
- return ResultSet .SORT_ASC
6302
- if not self .sort_reverse :
6303
- self .sort_reverse = True
6304
- self .sort (table )
6305
- return ResultSet .SORT_DESC
6306
- self .sort_reverse = False
6307
- self .sort_column = None
6308
- self .sort (table )
6309
- return ResultSet .SORT_NONE
6310
-
6311
-
6312
6077
class ReservedKeywordError (Exception ):
6313
6078
pass
6314
6079
0 commit comments