Django attribute is not up to date - python

I'm using Django / Postgresql, and I want to build some kind of media player behavior : start playing when asked, play until further command, and stop playing when asked.
My Django model is designed as follows :
a boolean attribute state
a function that switches the state value, and if it's true, calls the looping function
a function that loops while the state attribute is True, and stops when it is False.
The code is as follows:
state = models.BooleanField(default=False)
def switchState(self):
print 'switching from %s to %s' % (self.state, not(self.state))
self.state = not(self.state)
self.save()
if self.state :
self.loop()
def loop(self):
while self.state:
print 'looping'
time.sleep( 1 )
print 'stopped looping'
Behavior :
when I call switchState() on a model instance, state is set to True (I can see it in the database) and the loop() function starts printing lines
when I call switchState() again, state is set to False (again, I can see it in the database) but then, the loop() function does not stop. When I print state, its value is still True...
I can't get an up to date value of that damned state attribute.
I must be missing something, but what ?
Thanks for your help !

OK I finally found an answer. As Daniel Roseman told me, the problem wad due to the loop function, which was looping in its own transaction, not being able to see what happened in another transaction.
I first tried to force a refresh on my instance, with the new refresh_from_db() function, but that didn't work at all (I still don't get why, please tell me if you know).
As Daniel suggested, I read again the Django transaction doc, and finally found out a solution that seems to work. The idea is to allow the loop function to update the instance at each loop, using the with transaction.atomic() command.
def loop(self):
vTest = Source.objects.get(id=self.id)
while vTest:
with transaction.atomic():
vTest = Source.objects.get(id=self.id).state
print 'looping'
time.sleep( 1 )
print 'stopped looping'
Nb : you can't use
vTest = self.state
instead of
vTest = Source.objects.get(id=self.id).state
Maybe it's not the right way to do it, feel free to correct me if I'm wrong.

Related

Ensure query is atomic

Background
In my code below I have a function called process that does some stuff and when it is running i want to make sure it is not run concurrently. I have a table called creation_status where i set a time stamp anytime i start the process. The reason i use a time stamp is because it allows me to know what time i started this process in case i need to.
I always check if there is already a time stamp and if there is raise an exception to make sure i am not running this script concurrently.
Code
def is_in_process() -> bool:
status = db.run_query(sql="SELECT is_in_process FROM creation_status")
return False if status[0].is_in_process is None else True
def set_status() -> None:
db.execute(sql="UPDATE creation_status SET is_in_process = NOW()")
def delete_status() -> None:
db.execute(sql="UPDATE creation_status SET is_in_process = NULL")
def process():
if is_in_process():
raise Exception("Must not run concurrent process creations." )
set_status()
# stuff happens
delete_status()
Issue
I want to make sure my query is atomic to eliminate race conditions. It is possible that the by the time i check the function is_in_process and call the function set_status another script could get kicked off. How do i ensure both those things happen in one go so i avoid race conditions.
Please let me know if i can explain something more clear and i am open to all suggestions.
Don't use multiple steps when you don't need to.
UPDATE creation_status SET is_in_process = NOW() where is_in_process is null returning is_in_process
Then check to see if a row is returned.
Of course this should probably be done in the same transaction as the rest of the stuff, which we can't tell from your code, but then things will just block until the previous is done, rather than aborting.

Access an attribute of the same object in two functions running at the same time

Hello i am having a weird issue maybe some one can help,
I start by running 2 different function with the same argument which is an object that is already instantiated :
iotComponent.connectedSensors=sensorList
iotComponent.connectedHUIs=HUIList
Coap = multiprocessing.Process(target=runCoapSync,args=(iotComponent,))
huis = multiprocessing.Process(target=runHuis,args=(iotComponent,))
huis.start()
Coap.start()
then here are both functions :
async def runCoap(iotDevice):
context = await Context.create_client_context()
sensor=iotDevice.connectedSensors[0]
while True:
await asyncio.sleep(1)
sensor.sense()
lightMsg = iotDevice.applicationInterface.createMsg( sensor, iotDevice.communicationProtocol.name)
await iotDevice.communicationProtocol.sendMsg(context,lightMsg,"light")
def runHuis(iotDevice):
print("----------------1---------------")
LCD=iotDevice.connectedHUIs[0]
while True:
LCD.alertHuman(iotDevice.connectedSensors[0].data.value)
in the first function when sensor.sense() is called the value attribute inside data attribute of the sensor is updated.
But in the second function, iotDevice.connectedSensors[0].data.value is always equals to Zero. I find this behavior weird because this is the same object. Moreover if i add a line sensor.sense() in the second function the value gets updated but it is not the same as the value printed in first function.
EDIT 0 :
here is the sense() method :
def sense(self):
pinMode(self.pinNumber, "INPUT")
lightSensorValue = analogRead(self.pinNumber)
self.data.timestamp=str(round(time.time(), 3))
self.data.value=lightSensorValue
If someone as an idea that would be great !
SOLUTION : as said in the accepted answer i tried with threading and it worked like a charm :
Coap = threading.Thread(target=runCoapSync,args=(iotComponent,))
huis = threading.Thread(target=runHuis,args=(iotComponent,))
huis.start()
Coap.start()
See this answer. Essentially what's happening is that your data is "pickled" before being sent to the processes to have work done. When the objects are received, they're unpacked. Therefore, the objects are more cloned than passed around. Therefore, you're actually working with two separate copies of iotComponent, which explains why you can't actually see any change happening on one even though you "know" work is being done. There might be a way to do this, given this. However, it might be better to not use Process, but use Thread instead, see here. The difference is that, according to this, threads are better for I/O-bound operations, which your sensor certainly is.

How can I make this dictionary object keep changing under the while statement?

Originally the question was meant to be solved using recursion, but anyway, I just decided to use the while statement.
If recursion is necessary, please let me know.
This is just a part of my code, but I guess the problem is that the dictionaries employee_state and visitor_state keep changing but the changes are not applied to the while statement.
How can I fix that?
Plus, if you need more of my codes to suggest an answer, just comment me.
def order_process(self):
employee_state={}
visitor_state={}
for employee in self.employees:
employee_state[employee]=employee.state
for visitor in self.visitors:
visitor_state[visitor]=visitor.state
while WAITING in employee_state.values() and ARRIVAL in visitor_state.values():
print(employee_state)
print(visitor_state)
for visitor in self.visitors:
for employee in self.employees:
if employee.state == WAITING:
if visitor.state == ARRIVAL:
employee.cook(visitor)
employee_state[employee] = employee.state
visitor_state[visitor] = visitor.state
A quick and easy fix would be to change the while statement to simply be while True: and then at the end of the while block, add the following conditional
if WAITING not in employee_state.values() and ARRIVAL not in visitor_state.values():
break

Python not querying database in loop

I currently have a process running that should call a method every 10 seconds. I see that it actually calls the method at that interval, but it seems to not execute something in the code. Weird thing is, is that when I cancel the loop, and start it new it does actually do it the first time. Then when I keep it running it does not do anything.
def main():
try:
while True:
read()
time.sleep(10)
except KeyboardInterrupt:
pass
Above is the loop, and the code here is actually the beginning of the method that is being called, and I found out that it does not actually get results in the results, while the file has changed. In this case it gets data from a .json file
def read():
message = Query()
results = DB.search(message.pushed == False)
Am I overlooking something?
Solved. I had the DB declared globally and that did not go so well. It is being fixed by declaring it just before the statement.

Abort an insert from a before_insert event in sqlalchemy

I'm adding automatic validation to one of my models in a pyramid application with before_insert.
So far I've got this:
def Property_before_insert_listener(mapper, connection, target):
formvalidator = PropertySchema()
try:
return formvalidator.to_python(target.__table__.columns)
except formencode.Invalid as error:
print ("***************************************ERROR" + str(error))
event.listen(
Property, 'before_insert', Property_before_insert_listener)
Everything seems to be working fine, and I get the proper error printed out in the console. However, after handling the error, it continues with the insert. How do I stop the insert from happening?
In the sqlAlquemy documentation for Mapper Events you have something that might help you:
retval=False
when True, the user-defined event function must have a return value, the purpose of which is either to control subsequent event propagation, or to otherwise alter the operation in progress by the mapper. Possible return > values are:
sqlalchemy.orm.interfaces.EXT_CONTINUE - continue event processing normally.
sqlalchemy.orm.interfaces.EXT_STOP - cancel all subsequent event handlers in the chain.
other values - the return value specified by specific listeners.

Categories