How to add a hidden field value to QSqlRelationalTableModel during row creation? - python

I'm starting out with PyQt5. I've created a simple password management app that has a login page and a password listing page against various URLs. I'm unable to find a way to add the user_id value to the record with QSqlRelationalTableModel before it gets inserted into the DB. This is what I've done so far. Code examples would help me. Thanks.
self.model.beforeInsert.connect(self.beforeInsert)
def addRecord(self):
"""
Add a new record to the last row of the table.
"""
last_row = self.model.rowCount()
self.model.insertRow(last_row)
def beforeInsert(self, record):
print("[INFO] beforeInsert called", record)
record.setValue("user_id", 1)
done = self.model.insertRecord(-1, record)
# done is False at this point, however record.value("user_id") is 1

Related

How to create a trigger way database using peewee?

I have created a small script that is currently pulling the data from database and does this every minute. With that I then do a comparison to see whenever there has been a added row or deleted row in the database and that would print out whenver that happend. I have done something like this:
def main() -> None:
feed_urls: set[str] = set() # Set of urls to monitor
product_urls: set[str] = set() # Set of urls to monitor
while True:
db_urls = Feed.get_feeds(1) # Get from database where id equals store_id
diff = db_urls - feed_urls # Get the difference between the two sets
feed_urls &= db_urls # Set the urls to the intersection of the two sets
feed_urls |= db_urls # Set the urls to the union of the two sets
for url in diff:
print(f'New changes for URL: {url}')
and this is the peewee code:
from loguru import logger
from peewee import (
Model,
TextField,
BooleanField,
IntegrityError
)
from playhouse.pool import PooledPostgresqlDatabase
from config import configuration
# Create a database connection pool.
postgres_pool = PooledPostgresqlDatabase(
configuration.postgresql.database,
host=configuration.postgresql.host,
user=configuration.postgresql.user,
password=configuration.postgresql.password,
stale_timeout=30,
)
# ----------------------------------------------------------------------------------------------- #
class Feed(Model):
store_id = TextField(column_name='store_id')
url = TextField(column_name='url')
class Meta:
database = postgres_pool
db_table = "feed"
#classmethod
def get_feeds(cls, store_id: int) -> set:
try:
query = cls.select(cls.url).where(cls.store_id == store_id)
return set(url for url, in query.tuples().iterator())
except IntegrityError as err:
logger.info(f'[get_feeds] threw error {err}')
postgres_pool.rollback()
However I quickly realized that pulling a data every minute might not be the best idea and I did find that there is a possibility together with Peewee and postgresql where you can create a trigger function that instead of needing to pull every minute, it will instead be automatically triggered whenever there is actually a change.
I wonder how am I able to create a peewee function that triggers whenever there is a change in a database?

How can i lock some rows for inserting in Django?

I want to batch create users for admin. But, the truth is make_password is a time-consuming task. Then, if I returned the created user-password list util the new users are all created, it will let front user waiting for a long time. So, i would like to do somethings like code showed below. Then, I encountered a problem. Cause I can not figure out how to lock the user_id_list for creating, someone registered during the thread runtime will cause an Dulplicate Key Error error thing like that.
So, I am looking forward your good solutions.
def BatchCreateUser(self, request, context):
"""批量创建用户"""
num = request.get('num')
pwd = request.get('pwd')
pwd_length = request.get('pwd_length') or 10
latest_user = UserAuthModel.objects.latest('id') # retrieve the lastest registered user id
start_user_id = latest_user.id + 1 # the beginning user id for creating
end_user_id = latest_user.id + num # the end user id for creating
user_id_list = [i for i in range(start_user_id, end_user_id + 1)] # user id list for creating
raw_passwords = generate_cdkey(num, pwd_length, False) # generating passwords
Thread(target=batch_create_user, args=(user_id_list, raw_passwords)).start() # make a thread to perform this time-consuming task
user_password_list = list(map(list, zip(*[user_id_list, raw_passwords]))) # return the user id and password immediately without letting front user waiting so long
return {'results': user_password_list}

web2py: How to execute instructions before delete using SQLFORM.smartgrid

I use SQLFORM.smartgrid to show a list of records from a table (service_types). In each row of the smartgrid there is a delete link/button to delete the record. I want to executive some code before smartgrid/web2py actually deletes the record, for example I want to know if there are child records (services table) referencing this record, and if any, flash a message telling user that record cannot be deleted. How is this done?
db.py
db.define_table('service_types',
Field('type_name', requires=[IS_NOT_EMPTY(), IS_ALPHANUMERIC()]),
format='%(type_name)s',
)
db.define_table('services',
Field('service_name',requires=[IS_NOT_EMPTY(),IS_NOT_IN_DB(db,'services.service_name')]),
Field('service_type','reference service_types',requires=IS_IN_DB(db,db.service_types.id,
'%(type_name)s',
error_message='not in table',
zero=None),
ondelete='RESTRICT',
),
Field('interest_rate','decimal(15,2)',requires=IS_DECIMAL_IN_RANGE(0,100)),
Field('max_term','integer'),
auth.signature,
format='%(service_name)s',
)
db.services._plural='Services'
db.services._singular='Service'
if db(db.service_types).count() < 1:
db.service_types.insert(type_name='Loan')
db.service_types.insert(type_name='Contribution')
db.service_types.insert(type_name='Other')
controller
def list_services():
grid = SQLFORM.smartgrid(db.services
, fields = [db.services.service_name,db.services.service_type]
)
return locals()
view
{{extend 'layout.html'}}
{{=grid}}
There are two options. First, the deletable argument can be a function that takes the Row object of a given record and returns True or False to indicate whether the record is deletable. If it returns False, the "Delete" button will not be shown for that record, nor the delete operation be allowed on the server.
def can_delete(row):
return True if [some condition involving row] else False
grid = SQLFORM.smartgrid(..., deletable=can_delete)
Second, there is an ondelete argument that takes the db Table object and the record ID. It is called right before the delete operation, so to prevent the delete, you can do a redirect within that function:
def ondelete(table, record_id):
record = table(record_id)
if [some condition]:
session.flash = 'Cannot delete this record'
redirect(URL())
grid = SQLFORM.smartgrid(..., ondelete=ondelete)
Note, if the grid is loaded via an Ajax component and its actions are therefore performed via Ajax, using redirect within the ondelete method as shown above will not work well, as the redirect will have no effect and the table row will still be deleted from the grid in the browser (even though the database record was not deleted). In that case, an alternative approach is to return a non-200 HTTP response to the browser, which will prevent the client-side Javascript from deleting the row from the table (the delete happens only on success of the Ajax request). We should also set response.flash instead of session.flash (because we are not redirecting/reloading the whole page):
def ondelete(table, record_id):
record = table(record_id)
if [some condition]:
response.flash = 'Cannot delete this record'
raise HTTP(403)
Note, both the deletable and ondelete arguments can be dictionaries with table names as keys, so you can specify different values for different tables that might be linked from the smartgrid.
Finally, notice the delete URLs look like /appname/list_services/services/delete/services/[record ID]. So, in the controller, you can determine if a delete is being requested by checking if 'delete' in request.args. In that case, request.args[-2:] represents the table name and record ID, which you can use to do any checks.
From Anthony's answer I chose the second option and came up with the following:
def ondelete_service_type(service_type_table, service_type_id):
count = db(db.services.service_type == service_type_id).count()
if count > 0:
session.flash = T("Cant delete")
#redirect(URL('default','list_service_types#'))
else:
pass
return locals()
def list_service_types():
grid = SQLFORM.smartgrid(db.service_types
, fields = [db.service_types.type_name, db.services.service_name]
, ondelete = ondelete_service_type
)
return locals()
But, if I do this...
if count > 0:
session.flash = T("Cant delete")
else:
pass
return locals()
I get this error:
And if I do this:
if count > 0:
session.flash = T("Cant delete")
redirect(URL('default','list_service_types#')) <== please take note
else:
pass
return locals()
I get the flash error message Cant delete but the record appears deleted from the list, and reappears after a page refresh with F5 (apparently because the delete was not allowed in the database, which is intended).
Which one should I fix and how?
Note
If any of these issue is resolved I can accept Anthony's answer.

SQLAlchemy event how to get delete items info after db session commit

I am using SQLAlchemy event which is very very good, I can void a lots of repeated work.
Now I need to delete some items, and do some post work in after_delete listener after delete action, but, meanwhile in after_delete listener I need to using those deleted items info which cannot be accessed while the session had been committed, here's a piece of code .
def shop_delete_category(category_id):
item_category = ItemCategory.query.get(category_id)
if item_category is None:
result['code'] = 100
else:
deleted_category_id_items = ItemCategory.query.filter(ItemCategory.id == category_id).with_entities(ItemCategory.id).all()
# some other business logic
db.session.delete(item_category)
db.session.commit()
My event listener code:
#event.listens_for(ItemCategory, 'after_delete')
def after_delete_shop_category(mapper, connect, target):
# business logic
shop_category_delete_signal.send(target)
# here will need deleted_category_id_items
# how can I pass in ?
# logic will be like this
redis_cache_delete_category_ids(deleted_category_id_items)
I want to do some post work after delete action , and in delete action I will use deleted_category_id_items, my question is
how can I pass deleted_category_id_items the event? Or is there some better way to slove the problem ?
Edit: My own answer
found : before_delete listener can solve the problem
Edit:
Another question how can I pass my own parameter to a listener, like this
#event.listens_for(ItemCategory, 'after_delete')
def after_delete_shop_category(mapper, connect, target, *my_own_paramters):
# business logic
shop_category_delete_signal.send(target)
# here will need deleted_category_id_items
# how can I pass in ?
# logic will be like this
redis_cache_delete_category_ids(deleted_category_id_items)
# can use my_own_paramters do some work
Thanks very much !

Get author_id from mail_message in openERP

I'm trying to get a field from openERPs mail_message model using python code which is executed in a server action (so its not a module where I can debug! I cannot even print in this state) (when a new eMail is being fetched) but I am unable to get anything useful from it.
Basicly when someone is throwing me a email, a new Task is created by openERP. But the newely created ticket is not connected to the user which send me the mail.
When a new email is fetched, this server action gets executed.
In a table called mail_message you can then find the email (+ author_id, + email, + res_id (which is the id of the created Task), therefore I'd like to fetch the author_id from that table.
(A query would look like this:
SELECT author_id FROM mail_message WHERE type = 'email' AND res_id = '<Task.id>')
This is my current code
#Initialize object. That one points to the mail_message model.
mailMessage_obj = self.pool.get('mail.message')
#Created Id in project_task
myId = object.id
#browse whole object with that id
#message = mailMessage_obj.browse(cr,uid,[myId])
#Select field where
messageIds = mailMessage_obj.search(cr,uid,[('type','=','email'),('res_id','=',myId)],context=context)
if messageIds:
#messageRecord = mailMessage_obj.browse(cr,uid,[myId],context=context)
#object.write({'partner_id':messageRecord.author_id.id})
res = mailMessage_obj.read(messageIds, ['author_id'])
partnerId = res[0]
#Author id
#partnerId = message[0]['author_id']
#partnerId = message.author_id
#res = [(r['id'], r['author_id']) for r in messageRecord]
#partnerId = res
#partnerId = 259866
object.write({'partner_id':partnerId})
I dont know how to get my hands on the author_id properly. If I hardcode a ID and let it write to the database (last two lines) It'll work just fine, but I cant hardcode a users id. ;)
Could someone explain to me how its done correctly?
I dont know whether I should use .browse or .read or something else..
I think you have an error on you python method.
you wrote :
res = mailMessage_obj.read(messageIds, ['author_id'])
partnerId = res[0]
But read() method returns here a list of dict (because messageIds is a list). Then, you have not specified the field you wanted to retrieve from res variable, and finally, as author_id is a many2one, it returns something like this : (2, 'myusers').
You should have done :
res = mailMessage_obj.read(cr, uid, messageIds, ['author_id'])
partnerId = res[0]['author_id'][0]
Hope i helped you,
Greatings

Categories

Resources