Web2py: Incorrect row when representing a referenced field - reference

It's rare that I find an issue that hasn't already be answered but I've been searching for this for 3 days and haven't found anything yet.
I'm aiming to create a page for inputing records in a 'spreadsheet' like format. I've used inline editing in SQLFORM.grid from this slice.
The problem I'm having is that when one of the fields is a reference to another table, the row being use in the lambda function is taking the row of the reference table rather than the row in the grid.
Here is an example:
Model
db.define_table('people',
Field('name', 'string'),
format = '%(name)s',
)
db.define_table('animals',
Field('name', 'string'),
Field('pet_owner', 'reference people'),
format = '%(name)s',
)
Controller
def index():
#process submitted form
if len(request.post_vars) > 0:
print request.post_vars
for key, value in request.post_vars.iteritems():
(field_name,sep,row_id) = key.partition('_row_')
if row_id:
db(db.animals.id == row_id).update(**{field_name:value})
db.animals.name.represent = lambda value,row: SQLFORM.widgets.string.widget(db.animals.pet_owner,value, **{'_name':'name_row_%s' % row.id})
db.animals.pet_owner.represent = lambda value,row: SQLFORM.widgets.options.widget(db.animals.pet_owner,value, **{'_name':'pet_owner_row_%s' % row.id})
grid = SQLFORM.grid(db.animals,
selectable= lambda ids : redirect(URL('animals',vars=request._get_vars)),
)
grid.elements(_type='checkbox',_name='records',replace=None) #remove selectable's checkboxes
return dict(grid=grid)
At first it appears that the grid is working correctly. However, when inspecting the drop-downs for the reference fields, if two consecutive rows have the same value (e.g. two animals with the same owner) the same row is used in the name (pet_owner_row_1) which means that the value being passed to process the submitted form is not an integer (e.g. 3) but as the integers separated by pipes (e.g. '|3|3|').
I've confirmed that this is where the issue is by changing the represent to
db.animals.pet_owner.represent = lambda value,row: row
which shows the exact same row data for different animals.
Here is an image showing the inspector on the form items: http://i.stack.imgur.com/oFtF8.png
How can I get the row id of the grid's row rather than the id of the reference?
Any help is greatly appreciated!

This is a bug that has recently been fixed in the master branch but not released yet. In the meantime, a workaround is to temporarily change the field to an integer type:
db.animals.pet_owner.type = 'integer'
db.animals.pet_owner.represent = lambda value,row: SQLFORM.widgets.options.widget(
db.animals.pet_owner,value, **{'_name':'pet_owner_row_%s' % row.id})

Related

How does sibling or siblingAtRow() function works to retrieve the value from hidden Column in QTableWidget?

I have a database from which data is coming into a QTableWidget. The table in the database has the following Columns,
ID (Primary key, auto-increment value)
Name
Location
The QTableWidget has the following columns (that I have added)
ID (this column, I have hidden. and it contains the value of "ID" column from the Database Table)
Sr # (Represents the Row Number of the table)
Name (Contains "name" from the database table)
Location (Contains "Location from the database table)
Actions (Contains a Delete Button for that Row)
By hidden, I mean to say that I have made this column hidden using the folliwng command,
self.ui.table.setColumnHidden(0, True);
This is how I am populating my QTableWidget and creating a Delete Function,
def get_data(self):
mycursor = self.DB.cursor()
Subquery = "select id, name, location "
Subquery += " from tbl_person"
mycursor.execute(Subquery)
numcols = len(mycursor.fetchall()[0])
mycursor.execute(Subquery)
numrows = len(mycursor.fetchall())
self.ui.table.setRowCount(numrows)
self.ui.table.setColumnCount(numcols+2)
mycursor.execute(Subquery)
tablerow = 0
for row in mycursor.fetchall():
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
delete_button = QPushButton("Delete Data")
delete_button.clicked.connect(self.executeDeleteFunction)
# delete_button.setStyleSheet(delete_push_button) -> Only for styling
self.ui.table.setItem(tablerow, 0, PySide2.QtWidgets.QTableWidgetItem(str(row[0])))
self.ui.table.setItem(tablerow, 1, PySide2.QtWidgets.QTableWidgetItem(str(tablerow+1)))
self.ui.table.setItem(tablerow, 2, PySide2.QtWidgets.QTableWidgetItem(str(row[1])))
self.ui.table.setItem(tablerow, 3, PySide2.QtWidgets.QTableWidgetItem(str(row[2])))
self.ui.table.setCellWidget(tablerow, 4, delete_button)
tablerow += 1
self.ui.table.setColumnHidden(0, True)
#self.ui.registered_table.horizontalHeader().setSectionResizeMode(PySide2.QtWidgets.QHeaderView.Stretch)
self.ui.table.resizeColumnsToContents()
def executeDeleteFunction(self):
self.person_id = self.ui.table.selectionModel().selectedIndexes()[0]
self.person_id = self.person_id.row()
mycursor = self.DB.cursor()
sql = "delete from tbl_person where id = %s"
val = (id, )
mycursor.execute(sql, val)
print("Deletion Successful")
On the Deletion Function, what this code does is basically gets the value of the **Sr # ** Column from the QTableWidget and deletes the data according to that, i.e. it is getting me the value from the visible first column and not the actual first column. But, I want the data from the "ID" column of the QTableWidget which is hidden
I tried to look up on how to get the value from the first hidden column on the QTableWidget and ended up with this link: How to get data from hidden 'id' column in QtableWidget
This apparently solves my issue but I can not seem to make it work for my code. I don't want to retrieve values of multiple Rows but only of one row so how do I do this (as I am only deleting one row. But in the question mentioned, I believe that it is getting data from multiple rows due to that for each loop)?
Moreover, I tried to find help regarding the functionality of sibling function (which is provided in the answer of above question) however I could not find any good resource on this function (i.e. how to use this, or some practical example and etc.)
I tried the following with Sibling function to obtain the value of first hidden column of the Selected Row but it did not work,
self.value = self.table.selectedItems()[0]
self.value = sibling(self.value.row(), 0)
There are some conceptual problems with the given code.
First of all, the QtSql module should be preferred instead of artificially creating a model. For basic tables, QSqlTableModel is fine enough, while for custom queries, QSqlQueryModel is a good choice.
Now the problem is that UI-based selection is always based on visible items: if you select a row in a view that has hidden columns, you will not get the hidden indexes that belong to those columns.
In order to get the indexes (as in QModelIndex) of hidden columns on a table widget, the only way is the same for a table view: you need to access the model and get the index for the row, or you get the actual model index and then get the sibling (which is conceptually the same, as the underlying function does):
item = self.table.selectedItems()[0]
index = self.table.indexForItem(index)
firstRowIndex = index.sibling(index.row(), 0)
sqlIndex = firstRowIndex.data() # might be a string
Note that you can also use siblingAtColumn():
firstRowIndex = index.siblingAtColumn(0)
That's because when you create QTableWidget items, you're actually creating a new model, and the row for that model doesn't reflect the actual "row" of that index in the source model; items in the second row will return 1 for row(), even if their actual row is different, and that's because that item has been added as second to the table widget, since it's the second item in the query.
So, the solution is that you either get the incremental row value for the first column index sibling, or you use one of the predefined Sql models.
For simple models, the latter solution is fine enough, but if you need more complex models, the first is certainly more accurate and reliable.

Data appended to multiple dict values instead of one

driver_data_form = {
'forc_day_off':[],
'pref_day_off':[],
'pref_shift':{"day"+str(i):None for i in range(1,15)},
'route_data':[]
}
So I am creating the dict driver_data (seen below) by using driver_data_form (seen above)
driver_data = {str(i):driver_data_form for i in range(1,12)}
and accordingly populating it :
loop_list = [str(i) for i in range(1,13)]
1 for specific_driver in loop_list:
2 for driver in forced_day_off_data:
3 for day in driver:
4 if driver[day]=='1' and day != "driverid":
5 driver_data[specific_driver]['forc_day_off'].append(day)
forced_day_off_data looks like:
But for some reason, after the above loop is executed once (lines 2-5), and by placing a break point in line 2, I am getting all 11 values of my driver_data[forc_day_off] dictionary populated, instead of only the first one. It appears that the values of the first key are copied to all the rest of the values:
I debugged this piece of code many times and this behavior makes no sence to me? What could be causing this and how can I fix it?
The problem with your code is that python is using references to dicts and lists. When you do this
driver_data = {str(i):driver_data_form for i in range(1,12)}
It basically sets the same dict reference for all your keys so when you change one value you actually update for all the other keys since it's the same dict
For your code to work you need to do this:
driver_data = {str(i):{
'forc_day_off':[],
'pref_day_off':[],
'pref_shift':{"day"+str(j):None for j in range(1,15)},
'route_data':[]
} for i in range(1,12)}
This way you create a new dict for each element and you will update only the specific dict.
See this this link to better understand the difference.

How to add blanks for missing data after fetching data from nseindia.com

NIFTY option chain for expiry: 01-Oct-2020
The image above sums up the problem I am facing. The json file fetched has NO data at 13050 strike on the CE side. I am ok with not having data but I want to still see that strike in strike column with a blank row or 0 values for it on the respective side. JSON file has data for ALL the expiry dates possible.
JSON data file if needed:
The sample code (which won't work for you guys goes something like this:)
r = session.get(url, headers = url_headers).json()
with open('oidata.json', 'w') as files:
files.write(json.dumps(r, indent = 4, sort_keys= True ))# writing data in json file jsut for for reference
expiry_dates = r['records']['expiryDates']
# storing ONLY the data for given expiry
#Assume that we have at least one of the values for user or default expiry
if user_expiry in expiry_dates:
print('Using user Expiry: ', user_expiry)
ce_values = [items['CE'] for items in r['records']['data'] if 'CE' in items and user_expiry in items['CE']['expiryDate']]
pe_values = [items['PE'] for items in r['records']['data'] if 'PE' in items and user_expiry in items['PE']['expiryDate']]
else:
print('Using Default Expiry: ', default_expiry)
ce_values = [items['CE'] for items in r['records']['data'] if 'CE' in items and default_expiry in items['CE']['expiryDate']]
pe_values = [items['PE'] for items in r['records']['data'] if 'PE' in items and default_expiry in items['PE']['expiryDate']]
So, how do I add the blank row for missing data and align my strikes for both CE and PE, so that I can only have 1 STRIKE column???
Assuming that you make ce_values and pe_values into two dataframes, you can merge the two dataframe on strikePrice as follows
ce=pd.DataFrame(ce_values)
pe=pd.DataFrame(pe_values)
df=ce.merge(pe,on='strikePrice', how='outer', suffixes=["_ce","_pe"])
This will align both the dataframe on 'strikePrice' with an empty row where a ce or pe value is missing.
Let me know if this is what you are expecting.

python3 psycopg SQL identifier must be string

I am trying to reference dynamic tables and fields in a tkinter GUI project using MySQLdb. Using psycopg2.sql to handle an insert statement.
The user select a code, size and color and inputs a quantity. The table names are made up of the size and the code (eg. size-small and code-1111, table_name=small1111). Then the color is the column name and the quantity is an integer entered into the field. The inputs are saved in a dictionary (tdict) when the user selects them. And the dictionary elements are called to be saved in the database table.
table_name = tdict['Size']+tdict['Code']
stmnt = ("INSERT INTO {} (%s, Date) VALUES(%s, %s)").format(sql.Identifier((table_name, tdict['Color'])))
c.execute(sql.SQL(stmnt, (tdict['Quantity'], date)))
The insert query is giving me a TypeError
TypeError("SQL identifiers must be strings")
Can anyone please help? What am I doing wrong? How should the Identifier be made to behave as a string?
Note: I've tried to pass the Identifier elements through a str class but it didn't work. ie
stmnt = ("INSERT INTO {} (%s, Date) VALUES(%s, %s)").format(sql.Identifier((str(table_name, tdict['Color']))))
You are asking sql.Identifier() to create an identifier out of a tuple, e.g. ('small1111','magenta'). Because format() only substitutes into braces {} (and not %s), I think what you actually had in mind was this:
stmnt = sql.SQL("INSERT INTO {} ({}, Date) VALUES(%s %s)").format( sql.Identifier(table_name), sql.Identifier(tdict['Color']) )
I'd suggest you rethink your database design, though --- you should probably have columns named size, code, and color rather than separate tables and columns for each. That will prevent you from having to add a new column each time a new color or a new table for each new size or code. SELECT count(*) FROM inventory WHERE size = 'small' AND code = '1111' GROUP BY color seems preferable to having to create queries dynamically.
This error message will also appear when you have a typo error where you should have used sql.Literal('someFixedNumber'), but instead using sq.Identifier('someFixedNumber')

cannot set valid itemto QTableWidgetItem

I'm trying to populate two different QTableWidgets. For the first one it works find, but for the second one, it won't actaully set the items to the QTableWidget.
In the second, failing attempt, it does successfully create the item (both type(item) and item.text() work fine and return the correct values). However, when I try to add the item to the table, it says that table2.item(row, col) is NoneType. The rows and columns are created correctly before setting the item though.
working attempt:
item = QTableWidgetItem(self.fields[j].name())
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
table1.setItem(j,i,item)
failing attempt:
item = QTableWidgetItem(typ)
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
table2.setItem(row, col, item)
Neither can I see the difference between the two blocks, nor do I understand why it won't set the item to the TableWidget. Is there a geneal misunderstandng about how this works?

Resources