QTableView does not retain changes or update after adding records - sql

I am constructing a GUI which displays the contents of a SQL database table and which should allow addition and modification of records through the GUI, using the base PyQt4 classes QTableView and QSqlTableModel. I am having problems with editing the table view.
Here is a Minimal, Complete and Verifiable version of the complete code. It does require changing the login info of whatever SQL database you are using, but besides that the code is sufficient to run the interface:
import sys
import site
from subprocess import Popen
from PyQt4.QtCore import(Qt)
from PyQt4.QtGui import(QMainWindow, QApplication, QWidget, QPushButton, qApp,
QVBoxLayout, QTableView, QApplication)
from PyQt4.QtSql import(QSqlDatabase, QSqlQuery, QSqlQueryModel, QSqlTableModel)
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
# Open and connect to database - this needs to be changed for the particular db you are using
self.__database = QSqlDatabase.addDatabase('QPSQL')
self.__database.setHostName('localhost')
self.__database.setDatabaseName('~/Desktop/H2 testing/TESTING/db/db')
self.__database.setPort(5435)
self.__database.setUserName('grav')
self.__database.setPassword('XXXXXXXXXX')
self.__database.open()
ok = self.__database.open()
if ok == False:
print 'Could not open database'
print 'Text: ', self.__database.lastError().text()
print 'Type: ', str(self.__database.lastError().type())
print 'Number: ', str(self.__database.lastError().number())
print 'Loaded drivers:', str(QSqlDatabase.drivers())
# Create the QSqlTableModel using the database
self.modelDirections = QSqlTableModel(None, self.__database)
self.modelDirections.setTable('PUBLIC.DIRECTIONS')
self.modelDirections.setEditStrategy(QSqlTableModel.OnFieldChange)
self.modelDirections.select()
# Create the QTableView and connect to the QSqlTableModel
self.tableDirections = QTableView()
self.tableDirections.setModel(self.modelDirections)
# Create a QPushButton to add a row to the table
self.buttonAddDir = QPushButton('Add direction')
self.buttonAddDir.clicked.connect(self.createDirection)
# Set up the rest of the window with the QTableView and the QPushButton
vbox = QVBoxLayout()
vbox.addWidget(self.tableDirections)
vbox.addWidget(self.buttonAddDir)
stretchBox = QWidget()
stretchBox.setLayout(vbox)
self.setCentralWidget(stretchBox)
self.show()
def createDirection(self):
# Define and execute query to determine current max direction serial
model = QSqlQueryModel()
query = 'SELECT * FROM directions WHERE id=(SELECT MAX(id) FROM directions)'
model.setQuery(query)
if model.record(0).value('id').toString() == '':
newDirectionSerial = 0
else:
newDirectionSerial = int(model.record(0).value('id').toString()) + 1
# Define queries to insert new direction record
queryText = 'INSERT INTO public.directions (id, text, olddir, opposite) \
VALUES (%s, NULL, 1, NULL)' % (newDirectionSerial)
query = QSqlQuery()
query.exec_(queryText)
if __name__ == "__main__":
app = QApplication(sys.argv)
newWindow = Window()
sys.exit(app.exec_())
On loading the GUI, the table displays correctly in the QTableView. However, I have 2 problems:
I am able to double-click into a field in the table and begin editing, but then when I double-click into another field, anything I entered in the first field disappears.
When I edit a field and then switch to editing a new field, I get the message "Data changed" even though whatever I entered disappears.
My best guess here is that the data is being changed in the QSqlTableModel but then for some reason not translating through to the underlying database field, and when the view refreshes, it does so with an updated model from the still-empty database field.
When I add a new record using the button, the new record doesn't show up. If I close out the script and then start it up again, the new record shows up.
These may well be 2 separate problems but I have a feeling they're related.
I have managed to gather some more information about issue 1 since first posting this problem:
After connecting the QSqlTableModel.dataChanged signal to a reporter function, I'm able to confirm that
The signal is indeed firing when I edit the table-view fields,
The data is not being transferred from the model to the database, because of a problem with the query. The database is returning the following error:
ERROR: Column "text" not found; SQL statement: UPDATE PUBLIC.DIRECTIONS SET "text"='test' WHERE "id" = 1 AND "text" = 'constitutes' AND "dir" = 1 AND "opposite" = 2 [42122-164]
(42S22) QPSQL: Unable to create query
I'm able to access the database through another application and I test-ran variants of this query. Stripping the quotes around the field names and getting rid of the trailing [42122-164] produces a successful query and the behavior I've been looking for.
That's great - but I want the QSqlTableView to handle this, and don't know how. Anyone clues to how to modify the query behavior of the view (without completely rebuilding it) would be immensely appreciated!

Related

How to make antispam function discord.py?

I need antispam function on my discord server. Please help me. I tried this:
import datetime
import time
time_window_milliseconds = 5000
max_msg_per_window = 5
author_msg_times = {}
#client.event
async def on_ready():
print('logged in as {0.user}'.format(client))
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.playing,name="stack overflow"))
#client.event
async def on_message(message):
global author_msg_counts
ctx = await client.get_context(message)
author_id = ctx.author.id
# Get current epoch time in milliseconds
curr_time = datetime.datetime.now().timestamp() * 1000
# Make empty list for author id, if it does not exist
if not author_msg_times.get(author_id, False):
author_msg_times[author_id] = []
# Append the time of this message to the users list of message times
author_msg_times[author_id].append(curr_time)
# Find the beginning of our time window.
expr_time = curr_time - time_window_milliseconds
# Find message times which occurred before the start of our window
expired_msgs = [
msg_time for msg_time in author_msg_times[author_id]
if msg_time < expr_time
]
# Remove all the expired messages times from our list
for msg_time in expired_msgs:
author_msg_times[author_id].remove(msg_time)
# ^ note: we probably need to use a mutex here. Multiple threads
# might be trying to update this at the same time. Not sure though.
if len(author_msg_times[author_id]) > max_msg_per_window:
await ctx.send("Stop Spamming")
ping()
client.run(os.getenv('token'))
And it doesn't seem to work when I type the same message over and over again. Can you guys please help me? I need the good antispam function which will work inside on_message
I think the best thing you can do is to make an event on_member_join, which will be called every time user joins. Then in this event, you can make a list instead of variables that will save user id, and their current currency.
users_currency = ["user's id", "5$", "another user's id", "7$"] and so on. Next, I would recommend saving it to a text file.
Example code
global users_currency
users_currrency = []
#client.event
global users_currency
async def on_member_join(member): #on_member_join event
user = str(member.id) #gets user's id and changes it to string
users_currency.append(user) #adds user's id to your list
users_currency.append("0") #sets the currency to 0
Now if someone will join their id will appear in list and change their currency to 0.
How can you use assigned values in list
If you keep the code close to example higher then on users_currrency[0], users_currrency[2], [...]. You will get users' ids and on users_currrency[1], users_currrency[3], etc. You will get their currency. Then you can use on_message event or #client.command to make command that will look for user's id in list and change next value - their currency.
Saving it to a text file
You have to save it in a text file (Writing a list to a file with Python) and then make a function that will run at the start of the bot and read everything from the file and assign it inside your list.
Example code:
with open("users_currency.txt") as f:
rd=f.read()
changed_to_a_list=rd.split()
users_currency = changed_to_a_list

Flask and sqlite3 - does a table need to be populated with data before the 'insert into' sql command can work?

When creating a flask site with sqlite3, I noticed that if I tried to INSERTINTO (using a web-form) the database, when the database was empty, it wouldn't produce an error, but nothing would happen, the INSERT INTO (SQL) would simply not take effect.
I had to manually populate the database using a pre-made query, and then, without changing anything else in the code, the inserting-into the form worked fine.
Can anyone shed any light as to why this might have happened? I assume it isn't necessary for a database to be populated in order for it to be used/functional in flask/sqlite3.
I wondered if it had anything to do with my setup.
create-db.py
import sqlite3
db_locale='users.db' #flask will create this db if it doesn't exist
connie=sqlite3.connect(db_locale)
c=connie.cursor() #used to create commands
c.execute("""
CREATE TABLE comments
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
name TEXT,
comments TEXT
)
""")
connie.commit()
connie.close()
The addcomment() and insertcomment() methods in the main .py file
def addcomment():
if request.method=="GET":
return render_template('addcomment.html')
else:
user_details=(
request.form['title'],
request.form['name'],
request.form['comments']
)
insertcomment(user_details)
return render_template('addsuccess.html')
def insertcomment(user_details):
connie=sqlite3.connect(db_locale)
c=connie.cursor()
sql_execute_string='INSERT INTO comments (title,name,comments) VALUES (?,?,?)';
c.execute(sql_execute_string,user_details)
connie.commit()
connie.close()
print(user_details)

How to update/ delete a key from a context in Odoo 10?

Hi i am trying to delete a key value from the context for res.partner form view.
I opening the partner form view using controller function and trying to set phone number as default and its working fine. But when i try to create a new customer by clicking on the create button the phone number again auto-filled. In order to avoid this behaviour, in default_get function, i copied the context into another variable, removed the key value from the context using del context['cc_mobile']. And reassigned to self.env.context. But when i try to create a new customer, the deleted key value comes in the context again.
Controller.py
#http.route('/open_customer/<string:val>', type="http",method=['POST','GET'],website=False, auth="public")
def open_case_window(self,**kw):
mobile_no = kw.get('val')
action = request.env.ref('base.action_partner_form').sudo()
mobile_flag = 0
partner = 'res.partner'
partner_model = request.env[partner]
regex = re.match( '^(?:\01|02|03|04|06|07|09)\d*$', mobile_no)
if regex:
mobile_flag = 0
partner_id = partner_model.search([('phone', '=', mobile_no)]).id
else:
mobile_flag = 1
partner_id = partner_model.search([('mobile','=',mobile_no)]).id
if partner_id:
return werkzeug.utils.redirect('/web#id='+str(partner_id)+'&view_type=form&model='+partner)
else:
context = dict(action._context)
if mobile_flag == 0:
context.update({'cc_phone': mobile_no})
else:
context.update({'cc_mobile': mobile_no})
context.pop('lang')
url = werkzeug.utils.redirect('/web?debug=#view_type=form&model='+str(partner)+'&action=%s'%(action.id))
return url
ResPartner.py
#api.model
def default_get(self, fields):
context = self.env.context.copy()
print'default_get context',context
res = super(Partner, self).default_get(fields)
if 'cc_mobile' in context:
res.update({'mobile':context.get('cc_mobile')})
if 'cc_phone' in context:
res.update({'phone':context.get('cc_phone')})
if context.get('cc_mobile'):
del context['cc_mobile']
if context.get('cc_phone'):
del context['cc_phone']
self.env.context = context
print'self.env.context after',self.env.context
action = self.env.ref('base.action_partner_form').sudo()
action.env.context = self.env.context
return res
You cannot remove a key of action context from python side, because it's in the client side. when ever you call the server like search in many2one field, create a record in fly you will see this context comeback again every time (The way Odoo work).
What you need is something that will be used for one time, I think you need some kind of persistence for example:
dummy model that contains user_id, model_name, value, active fields so in the controller you create a record for default value for that specific user.
get that value by overriding default_get by searching with user_id and model_name field and hide that value or delete it.
this way when yo hit create button or create contact in fly when you search for the value it will be gone so it will not be used a second time.
This a simple Idea and easy to implement, you need to handle some cases to prevent user from saving two default value if some interruption happens should not be hard.
Edit
After second thought to prevent any error when you create a record just pass it's ID in the context with a special key, then use That Id to retrieve it, use it then delete it. easier, safer and no need for search.

Sqlite3 not inserting values into the database

I have made a program allows a user to manipulate a database, I have a part where the user can add multiple entries into a certain database (anywhere between 1 to 10 entries) I have created another program (below) that I use to "reset" the database, using similar principles to what my main program does.
import sqlite3, time
schoolDBConn = sqlite3.connect("SCHOOL_DB.db")
schoolDBCursor = schoolDBConn.cursor()
schoolDBCursor.execute(
"""
CREATE TABLE IF NOT EXISTS USER_DETAILS
(
username text,
password text,
clearance int,
classes int
)
"""
)
schoolDBCursor.execute("SELECT * FROM USER_DETAILS")
print(schoolDBCursor.fetchall())
if input("Delete all user_details?").upper() == "Y":
schoolDBCursor.execute("DELETE FROM USER_DETAILS")
if input("add items to user_details?").upper() == "Y":
user_list = [("foo","bar",3,0),("bar","foo",3,0),("Terri","Pass",2,0),
("Chris","Pass",2,0),("Tony","Pass",2,0),("Emma","Pass",2,0),
("Thomas","Pass",1,0),("Penny","Pass",1,0),("Kamryn","Pass",1,0),
("Kelsie","Pass",1,0),("James","Pass",1,0),("Connor","Pass",1,0),
("Steve","Pass",1,0),("Bob","Pass",2,0),("Elon","Pass",1,0)]
for i in user_list:
schoolDBCursor.execute("""INSERT INTO USER_DETAILS VALUES (?,?,?,?)""",
(i[0],i[1],i[2],i[3]))
schoolDBCursor.execute("SELECT * FROM USER_DETAILS")
print(schoolDBCursor.fetchall())
If I run this function, all the items in the existing database are printed, then if I choose to only delete what exists in the database, the print function returns nothing. If I choose to delete and then "add" the items to the database, the print function returns the items I added
However, when I re-run the program, the first print statement returns the original items in the database (not the deleted table, or the items in the users_list) and this is of course not helpful.
My main program is affected by this too, and it causes problems as I need data to be updated quickly.
I also use an application "SQlite manager" to view my tables, and they do not update in that either
Turns out I did not commit the data, I had to do:
schoolDBConn.commit()
or
with schoolDBConn:

how to use django.core.management.sql.sql_create?

I want to use this function:
django.core.management.sql.sql_create in my view, to get the "CREATE" statements
the function gets 3 arguments:
app, style, connection
what is "app"?? is it a specific object or just the app name?!
I know style is something to do with colors... I reckon django.core.management.color.colorstyle() should work..
what about connection, how do I get this one?
thanks
=========================== edited from here down
ok, after some time, I figured the things, this is what I ended up with:
def sqldumper(model):
"""gets a db model, and returns the SQL statements to build it on another SQL-able db"""
#This is how django inserts a new record
#u'INSERT INTO "polls_poll" ("question", "pub_date") VALUES (GE!!, 2011-05-03 15:45:23.254000)'
result = "BEGIN;\n"
#add CREATE TABLE statements
result+= '\n'.join(sql_create(models.get_app('polls'), color_style(), connections.all()[0]))+"\n"
result+= '\n'.join(sql_custom(models.get_app('polls'), color_style(), connections.all()[0]))+"\n"
result+= '\n'.join(sql_indexes(models.get_app('polls'), color_style(), connections.all()[0]))+"\n"
result+= '\n'
#add INSERT INTO staetements
units = model.objects.all().values()
for unit in units:
statement = "INSERT INTO yourapp.model "+str(tuple(unit.keys()))+" VALUES " + str(tuple(unit.values()))+"\n"
result+=statement
result+="\nCOMMIT;\n"
return result.encode('utf-8')
it's still a bit weird, because you get the CREATE TABLE for the whole app, but the INSERT INTO only for the model you ask.... but it's fixable from here
It's the models.py module found in one of your apps:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'myproject.myapp',
)
When I printed it, I got < module 'myproject.myapp.models' from '...' >. I used the ellipses instead of typing the entire models.py file's path.