Check for values return from EnumValue - python-2.7

I'm reading values from registry using EnumValue. I want to check the existence of all values from the registry and pop up error message if any value is empty.
key = OpenKey(HKEY_CURRENT_USER, r'Software\myapp\path', 0,KEY_READ)
for i in range(0,6):
n,v,t = EnumValue(key,i)
value = str(v).split(',')
if (value[0:] == ''):
dlg = wx.MessageDialog(None,'value is empty','Alert',wx.OK|wx.ICON_ERROR)
dlg.ShowModal()
value returns
['1000']
['10MS/s']
['Edge']
['500']
['']
['Rise']
How can i have all the values return from EnumValue in a single list so that i can iterate this list and find if any value is empty?
Tried ','.join([value]) but didn't work!

I found answer myself
import wx
from _winreg import *
empty = False
key = OpenKey(HKEY_CURRENT_USER, r'Software\myapp\path', 0,KEY_READ)
for i in range(0,6):
n,v,t = EnumValue(key,i)
value = str(v)
if value == "":
empty = True
break
if empty:
dlg = wx.MessageDialog(None,'value is empty','Alert',wx.OK|wx.ICON_ERROR)
dlg.ShowModal()

Related

inserting a data in a formset passed by a form

hi I have this error in inserting a data in a formset passed by a form this is the error that appears in my browser:
NOT NULL constraint failed: devtest_datigruppi.gruppi_scheda_id
it practically fails to see this change: groups.gruppi_scheda = Schede.objects.get (tab_name = tabName) but via print the right thing appears to me
schedaName = schede_form.cleaned_data['nome_scheda']
scheda = schede_form.save(commit = False)
scheda.utente = request.user
scheda.save()
#gruppi
if gruppi_formset.is_valid():
for gruppi in gruppi_formset:
gruppi.save(commit = False)
gruppi.gruppi_scheda = Schede.objects.get(nome_scheda = schedaName)
//print(gruppi.gruppi_scheda)
gruppi.save()
You have to assign the return value of gruppi.save(commit=False) into a variable and update the gruppi_scheda property there:
gruppi_instance = gruppi.save(commit=False)
gruppi_instance.gruppi_scheda = Schede.objects.get(nome_scheda = schedaName)
gruppi_instance.save()

Using Python Tkinter .config() method

I am trying to use the Python Tkinter .config() method to update some message text. I can't get it to work. What might I be doing wrong (see the update_message method):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
import threading
import Queue
# styles
BACKROUND_COLOR = '#000000'
TYPEFACE = 'Unit-Bold'
FONT_SIZE = 50
TEXT_COLOR = '#777777'
TEXTBOX_WIDTH = 400
# text
TITLE = 'listen closely'
SCORE_MESSAGE = 'your score:\n '
END_MESSAGE = 'too loud!\ntry again'
# configuration
DEVICE = 'hw:1' # hardware sound card index
CHANNELS = 1
SAMPLE_RATE = 8000 # Hz // 44100
PERIOD = 256 # Frames // 256
FORMAT = aa.PCM_FORMAT_S8 # Sound format
NOISE_THRESHOLD = 3
class Display(object):
def __init__(self, parent, queue):
self.parent = parent
self.queue = queue
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(
parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title(TITLE)
parent.configure(background=BACKROUND_COLOR)
parent.displayFont = tkFont.Font(family=TYPEFACE, size=FONT_SIZE)
self.process_queue()
def process_queue(self):
try:
score = self.queue.get(0)
self.print_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def update_queue(self):
try:
score = self.queue.get(0)
self.update_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def print_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message = tk.Message(
self.parent, text=displayString, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR, width=TEXTBOX_WIDTH, justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def update_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message.config(text=displayString)
def setup_audio(queue, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol = audioop.rms(data, 2)
scaled_vol = max_vol // 4680
print scaled_vol
if scaled_vol <= 3:
# Too quiet, ignore
continue
queue.put(scaled_vol)
except audioop.error, e:
if e.message != "not a whole number of frames":
raise e
def main():
root = tk.Tk()
queue = Queue.Queue()
window = Display(root, queue)
stop_event = threading.Event()
audio_thread = threading.Thread(target=setup_audio,
args=[queue, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
pass
if __name__ == '__main__':
main()
I don't want to be laying down a new message every time I update. If the .config() doesn't work, is there another method to update the text configuration of the message?
I would use string variables, first create your string variable then set it to want you want it to display at the start next make your object and in text put the sting variable then when you want to change the text in the object change the string variable.
self.messaget = StringVar()
self.messaget.set("")
self.message = tk.Message(
self.parent, textvariable=self.messaget, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR,
width=TEXTBOX_WIDTH, justify="c").grid()
#note renember to palce the object after you have created it either using
#.grid(row = , column =) or .pack()
#note that it is textvariable instead of text if you put text instead it will run but
#but will show PY_Var instead of the value of the variable
edit
to change the text without recreating the object you do the name of the string variable you have used and .set
self.messaget.set("hi")

Setting and Retrieving Data with TkInter

I just ran into some strange behavior that has me stumped. I'm writing a simple little GUI for some in-house data processing. I want to allow a user to switch between a few different data-processing modes and input some parameters which define how the data is processed for each mode. The problem is that when the user inputs new parameters, the app ignores requests to switch modes.
The code below replicates the issue. I apologize for the size, this was the shortest code that replicates the problem.
import Tkinter as Tk
class foo(Tk.Frame):
def __init__(self):
self.master = master =Tk.Tk()
Tk.Frame.__init__(self,self.master) #Bootstrap
#Here mode and parameters as key, value pairs
self.data = {'a':'Yay',
'b':'Boo'
}
self.tex = Tk.Text(master=master)
self.tex.grid(row=0,column=0,rowspan=3,columnspan=4)
self.e = Tk.Entry(master=master)
self.e.grid(row=3,column=0,columnspan=4)
self.sv =Tk.StringVar()
self.sv.set('a')
self.b1 = Tk.Radiobutton(master=master,
text = 'a',
indicatoron = 0,
variable = self.sv,
value = 'a')
self.b2 = Tk.Radiobutton(master=master,
text = 'b',
indicatoron = 0,
variable = self.sv,
value = 'b')
self.b3 = Tk.Button(master = master,
text='Apply',command=self.Apply_Func)
self.b4 = Tk.Button(master = master,
text='Print',command=self.Print_Func)
self.b1.grid(row=4,column=0)
self.b2.grid(row=4,column=1)
self.b3.grid(row=4,column=2)
self.b4.grid(row=4,column=3)
def Apply_Func(self):
self.innerdata = self.e.get()
def Print_Func(self):
self.tex.insert(Tk.END,str(self.innerdata)+'\n')
#This is how I'm retrieving the user selected parameters
#property
def innerdata(self):
return self.data[self.sv.get()]
#And how I'm setting the user defined parameters
#innerdata.setter
def innerdata(self,value):
self.data[self.sv.get()] = value
if __name__ == "__main__":
app = foo()
app.mainloop()
Expected behavior:
1) Press button 'a' then 'print' prints:
Yay
2) Press button 'b' then 'print' prints:
Boo
3) Type 'Zep Rocks' into the entry field and press apply
4) Pressing 'print' now yields
Zep Rocks
5) Pressing 'a' then 'print' should yield
Yay
But instead yields
Zep Rocks
Which might be true, but not desired right now. What is going on here?
Edit: I have some new information. Tk.Frame in python 2.7 is not a new-style class. It isn't friendly with descriptors, so rather than interpreting the '=' as a request to use the foo.innerdata's __set__ method, it just assigns the result of self.e.get() to innerdata.
ARGLEBARGLE!!!
Still an open question: how do I get this to do what I want in a clean manner?
So the core problem is that Tk.Frame doesn't subclass from object, so it is not a new-style python class. Which means it doesn't get down with descriptors like I was trying to use. One solution that I found is to subclass my app from object instead.
Code that solves my problem is below:
import Tkinter as Tk
class foo(object):
def __init__(self,master):
self.master = master #Bootstrap
self.mainloop = master.mainloop
self.data = {'a':{'value':7,'metavalue':False},
'b':{'value':'Beeswax','metavalue':True}
}
self.tex = Tk.Text(master=master)
self.tex.grid(row=0,column=0,rowspan=3,columnspan=4)
self.e = Tk.Entry(master=master)
self.e.grid(row=3,column=0,columnspan=4)
self.sv =Tk.StringVar()
self.sv.set('a')
self.b1 = Tk.Radiobutton(master=master,
text = 'a',
indicatoron = 0,
variable = self.sv,
value = 'a')
self.b2 = Tk.Radiobutton(master=master,
text = 'b',
indicatoron = 0,
variable = self.sv,
value = 'b')
self.b3 = Tk.Button(master = master,text='Apply',command=self.Apply_Func)
self.b4 = Tk.Button(master = master,text='Print',command=self.Print_Func)
self.b1.grid(row=4,column=0)
self.b2.grid(row=4,column=1)
self.b3.grid(row=4,column=2)
self.b4.grid(row=4,column=3)
def Apply_Func(self):
self.innerdata = self.e.get()
def Print_Func(self):
self.tex.insert(Tk.END,str(self.innerdata)+'\n')
#property
def innerdata(self):
return self.data[self.sv.get()]
#innerdata.setter
def innerdata(self,value):
self.data[self.sv.get()] = value
if __name__ == "__main__":
master = Tk.Tk()
app = foo(master)
app.mainloop()

python script to read and write a path to registry

I have developed a python script where i have a setting window which has the options to select the paths for the installation of software.When clicked on OK button of the setting window, i want to write all the selected paths to the registry and read the same when setting window is opened again.
My code looks as below.
def OnOk(self, event):
data1=self.field1.GetValue() #path selected in setting window
aReg = ConnectRegistry(None,HKEY_LOCAL_MACHINE)
keyVal=OpenKey(aReg,r"SOFTWARE\my path to\Registry", 0,KEY_WRITE)
try:
SetValueEx(keyVal,"Log file",0,REG_SZ,data1)
except EnvironmentError:
pass
CloseKey(keyVal)
CloseKey(aReg)
I get a error like below:
Traceback (most recent call last):
File "D:\PROJECT\project.py", line 305, in OnOk
keyVal=OpenKey(aReg,r"SOFTWARE\my path to\Registry", 0,KEY_WRITE)
WindowsError: [Error 5] Access is denied
And to read from registry,the saved registry has to show up in the setting window.I tried with the below code.Though its working but not satisfied with the way i programmed it.Help me out for the better solution
key = OpenKey(HKEY_CURRENT_USER, r'Software\my path to\Registry', 0, KEY_READ)
for i in range(4):
try:
n,v,t = EnumValue(key,i)
if i==0:
self.field2.SetValue(v)
elif i==1:
self.field3.SetValue(v)
elif i==2:
self.field4.SetValue(v)
elif i==3:
self.field1.SetValue(v)
except EnvironmentError:
pass
CloseKey(key)
#Python3 version of hugo24's snippet
import winreg
REG_PATH = r"Control Panel\Mouse"
def set_reg(name, value):
try:
winreg.CreateKey(winreg.HKEY_CURRENT_USER, REG_PATH)
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0,
winreg.KEY_WRITE)
winreg.SetValueEx(registry_key, name, 0, winreg.REG_SZ, value)
winreg.CloseKey(registry_key)
return True
except WindowsError:
return False
def get_reg(name):
try:
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0,
winreg.KEY_READ)
value, regtype = winreg.QueryValueEx(registry_key, name)
winreg.CloseKey(registry_key)
return value
except WindowsError:
return None
#Example MouseSensitivity
#Read value
print (get_reg('MouseSensitivity'))
#Set Value 1/20 (will just write the value to reg, the changed mouse val requires a win re-log to apply*)
set_reg('MouseSensitivity', str(10))
#*For instant apply of SystemParameters like the mouse speed on-write, you can use win32gui/SPI
#http://docs.activestate.com/activepython/3.4/pywin32/win32gui__SystemParametersInfo_meth.html
Same as #Aramanethota but with pep8 and func def for easy usage.
REG_PATH = r"SOFTWARE\my_program\Settings"
def set_reg(name, value):
try:
_winreg.CreateKey(_winreg.HKEY_CURRENT_USER, REG_PATH)
registry_key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, REG_PATH, 0,
_winreg.KEY_WRITE)
_winreg.SetValueEx(registry_key, name, 0, _winreg.REG_SZ, value)
_winreg.CloseKey(registry_key)
return True
except WindowsError:
return False
def get_reg(name):
try:
registry_key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, REG_PATH, 0,
_winreg.KEY_READ)
value, regtype = _winreg.QueryValueEx(registry_key, name)
_winreg.CloseKey(registry_key)
return value
except WindowsError:
return None
Python script to read from registry is as follows:
try:
root_key=OpenKey(HKEY_CURRENT_USER, r'SOFTWARE\my path to\Registry', 0, KEY_READ)
[Pathname,regtype]=(QueryValueEx(root_key,"Pathname"))
CloseKey(root_key)
if (""==Pathname):
raise WindowsError
except WindowsError:
return [""]
Python script to write to the registry is:
try:
keyval=r"SOFTWARE\my path to\Registry"
if not os.path.exists("keyval"):
key = CreateKey(HKEY_CURRENT_USER,keyval)
Registrykey= OpenKey(HKEY_CURRENT_USER, r"SOFTWARE\my path to\Registry", 0,KEY_WRITE)
SetValueEx(Registrykey,"Pathname",0,REG_SZ,Pathname)
CloseKey(Registrykey)
return True
except WindowsError:
return False
Hope it helps you all.Cheers:)
Reading registry keys:
def read(path, root=HKEY_CURRENT_USER):
path, name = os.path.split(path)
with suppress(FileNotFoundError), OpenKey(root, path) as key:
return QueryValueEx(key, name)[0]
And writing:
def write(path, value, root=HKEY_CURRENT_USER):
path, name = os.path.split(path)
with OpenKey(root, path, 0, KEY_WRITE) as key:
SetValueEx(key, name, 0, REG_SZ, value)
Extended for type handling. Provide type as argument, match current type in registry or python value type.
def write(path, value, root=HKEY_CURRENT_USER, regtype=None):
path, name = os.path.split(path)
with OpenKey(root, path, 0, KEY_WRITE|KEY_READ) as key:
with suppress(FileNotFoundError):
regtype = regtype or QueryValueEx(key, name)[1]
SetValueEx(key, name, 0, regtype or REG_DWORD if isinstance(value, int) else REG_SZ, str(value) if regtype==REG_SZ else value)
NOTE: Use of contextlib.suppress() (available since python 3.4) can be replaced by try..except..pass for older versions. The context manager interface for winreg was introduced in python 2.6.
Looks like you don't have permission to edit the Registry. Incase if you are Admin, Please run this script in Elevated state.
Here is a class I wrote (python 2) which has the ability to restore state when you finish manipulating the registry. The class was not tested properly so it may contain some bugs:
import _winreg as winreg
class Registry(object):
def __init__(self, restore_state=False):
self.m_backup = {}
self.m_restore_state = restore_state
def get_key(self, hkey, subkey, access, create_if_doesnt_exist=True):
created_key = False
registry_key = None
try:
registry_key = winreg.OpenKey(hkey, subkey, 0, access)
except WindowsError:
try:
if create_if_doesnt_exist:
registry_key = winreg.CreateKey(hkey, subkey)
if registry_key not in self.m_backup:
self.m_backup[registry_key] = ({}, (hkey, subkey))
else:
registry_key = None
except WindowsError:
if registry_key:
self.close_key(registry_key)
raise Exception('Registry does not exist and could not be created.')
return registry_key
def close_key(self, registry_key):
closed = False
if registry_key:
try:
winreg.CloseKey(registry_key)
closed = True
except:
closed = False
return closed
def get_reg_value(self, hkey, subkey, name):
value = None
registry_key = self.get_key(hkey, subkey, winreg.KEY_READ, False)
if registry_key:
try:
value, _ = winreg.QueryValueEx(registry_key, name)
except WindowsError:
value = None
finally:
self.close_key(registry_key)
return value
def set_reg_value(self, hkey, subkey, name, type, value):
registry_key = self.get_key(hkey, subkey, winreg.KEY_WRITE, True)
backed_up = False
was_set = False
if registry_key:
if self.m_restore_state:
if registry_key not in self.m_backup:
self.m_backup[registry_key] = ({}, None)
existing_value = self.get_reg_value(hkey, subkey, name)
if existing_value:
self.m_backup[registry_key][0][name] = (existing_value, type, False)
else:
self.m_backup[registry_key][0][name] = (None, None, True)
backed_up = True
try:
winreg.SetValueEx(registry_key, name, 0, type, value)
was_set = True
except WindowsError:
was_set = False
finally:
if not backed_up:
self.close_key(registry_key)
return was_set
def restore_state(self):
if self.m_restore_state:
for registry_key, data in self.m_backup.iteritems():
backup_dict, key_info = data
try:
for name, backup_data in backup_dict.iteritems():
value, type, was_created = backup_data
if was_created:
print registry_key, name
winreg.DeleteValue(registry_key, name)
else:
winreg.SetValueEx(registry_key, name, 0, type, value)
if key_info:
hkey, subkey = key_info
winreg.DeleteKey(hkey, subkey)
except:
raise Exception('Could not restore value')
self.close_key(registry_key)
def __del__(self):
if self.m_restore_state:
self.restore_state()
for Creating / writing values in registry key:
from winreg import*
import winreg
keyVal = r'SOFTWARE\\python'
try:
key = OpenKey(HKEY_LOCAL_MACHINE, keyVal, 0, KEY_ALL_ACCESS)
except:
key = CreateKey(HKEY_LOCAL_MACHINE, keyVal)
SetValueEx(key, "Start Page", 0, REG_SZ, "snakes")
CloseKey(key)
If access denied - try running the command (CMd or IDE) in administrative mode
for reading value in registry-key
from winreg import*
Registry = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
RawKey = OpenKey(Registry, "SOFTWARE\\python")
try:
i = 0
while 1:
name, value, type = EnumValue(RawKey, i)
print("name:",name,"value:", value,"i:", i)
i += 1
except WindowsError:
print("")
My solution:
def add_key(name,pathh):
try:
keyval=r"System\my path\Register"
if not os.path.exists("keyval"):
key = winreg.CreateKey(winreg.HKEY_CURRENT_USER,keyval)
Registrykey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"System\my path\Register", 0,winreg.KEY_WRITE)
winreg.SetValueEx(Registrykey,name,1,winreg.REG_SZ,pathh)
winreg.CloseKey(Registrykey)
return True
except WindowsError:
return False
The 'winreg' module is so ...strange working module, so, I wrote a class called 'WindowsRegistry' for working with Windows registry and 'winreg' module easier. I hope it will be more usefull:
import winreg
import re
class WindowsRegistry:
"""Class WindowsRegistry is using for easy manipulating Windows registry.
Methods
-------
query_value(full_path : str)
Check value for existing.
get_value(full_path : str)
Get value's data.
set_value(full_path : str, value : str, value_type='REG_SZ' : str)
Create a new value with data or set data to an existing value.
delete_value(full_path : str)
Delete an existing value.
query_key(full_path : str)
Check key for existing.
delete_key(full_path : str)
Delete an existing key(only without subkeys).
Examples:
WindowsRegistry.set_value('HKCU/Software/Microsoft/Windows/CurrentVersion/Run', 'Program', r'"c:\Dir1\program.exe"')
WindowsRegistry.delete_value('HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Run/Program')
"""
#staticmethod
def __parse_data(full_path):
full_path = re.sub(r'/', r'\\', full_path)
hive = re.sub(r'\\.*$', '', full_path)
if not hive:
raise ValueError('Invalid \'full_path\' param.')
if len(hive) <= 4:
if hive == 'HKLM':
hive = 'HKEY_LOCAL_MACHINE'
elif hive == 'HKCU':
hive = 'HKEY_CURRENT_USER'
elif hive == 'HKCR':
hive = 'HKEY_CLASSES_ROOT'
elif hive == 'HKU':
hive = 'HKEY_USERS'
reg_key = re.sub(r'^[A-Z_]*\\', '', full_path)
reg_key = re.sub(r'\\[^\\]+$', '', reg_key)
reg_value = re.sub(r'^.*\\', '', full_path)
return hive, reg_key, reg_value
#staticmethod
def query_value(full_path):
value_list = WindowsRegistry.__parse_data(full_path)
try:
opened_key = winreg.OpenKey(getattr(winreg, value_list[0]), value_list[1], 0, winreg.KEY_READ)
winreg.QueryValueEx(opened_key, value_list[2])
winreg.CloseKey(opened_key)
return True
except WindowsError:
return False
#staticmethod
def get_value(full_path):
value_list = WindowsRegistry.__parse_data(full_path)
try:
opened_key = winreg.OpenKey(getattr(winreg, value_list[0]), value_list[1], 0, winreg.KEY_READ)
value_of_value, value_type = winreg.QueryValueEx(opened_key, value_list[2])
winreg.CloseKey(opened_key)
return value_of_value
except WindowsError:
return None
#staticmethod
def set_value(full_path, value, value_type='REG_SZ'):
value_list = WindowsRegistry.__parse_data(full_path)
try:
winreg.CreateKey(getattr(winreg, value_list[0]), value_list[1])
opened_key = winreg.OpenKey(getattr(winreg, value_list[0]), value_list[1], 0, winreg.KEY_WRITE)
winreg.SetValueEx(opened_key, value_list[2], 0, getattr(winreg, value_type), value)
winreg.CloseKey(opened_key)
return True
except WindowsError:
return False
#staticmethod
def delete_value(full_path):
value_list = WindowsRegistry.__parse_data(full_path)
try:
opened_key = winreg.OpenKey(getattr(winreg, value_list[0]), value_list[1], 0, winreg.KEY_WRITE)
winreg.DeleteValue(opened_key, value_list[2])
winreg.CloseKey(opened_key)
return True
except WindowsError:
return False
#staticmethod
def query_key(full_path):
value_list = WindowsRegistry.__parse_data(full_path)
try:
opened_key = winreg.OpenKey(getattr(winreg, value_list[0]), value_list[1] + r'\\' + value_list[2], 0, winreg.KEY_READ)
winreg.CloseKey(opened_key)
return True
except WindowsError:
return False
#staticmethod
def delete_key(full_path):
value_list = WindowsRegistry.__parse_data(full_path)
try:
winreg.DeleteKey(getattr(winreg, value_list[0]), value_list[1] + r'\\' + value_list[2])
return True
except WindowsError:
return False

Denormalising field in django

I'm finding it difficult to denormalise a field in a django model. I have:
class AnswerSet(models.Model):
title = models.CharField(max_length=255)
num_answers = models.PositiveIntegerField(editable=False, default=0)
answers = models.ManyToManyField(Answer, through='AnswerSetAnswer')
...
class AnswerSetAnswer(models.Model):
answer = models.ForeignKey(Answer)
answer_set = models.ForeignKey(AnswerSet)
...
I want num_answers to contain a count of the number of answers in the set.
If 5 answers are initially associated with the AnswerSet "Food" and I edit one so it becomes associated with the AnswerSet "Colours", how can I recalculate the number of answers in the AnswerSet with "Food"? All the signals only seem to send the new data so I can't just override the save method.
I've tried using the m2m_changed signal, but it never gets called when I edit relationships through the admin form.
Here's my code anyway:
def update_answer_set_num_answers(sender, **kwargs):
"""
Updates the num_answers field to reflect the number of answers
associated with this AnswerSet
"""
instance = kwargs.get('instance', False)
print "no instance" # never gets here
if not instance:
return
action = kwargs.get('action')
print "action: ", action
if (action != 'pre_remove' and action != 'pre_add' and action != 'clear'):
return
reverse = kwargs.get('reverse')
if reverse:
answer_set = instance.answer_set
else:
answer_set = instance.answer_set
num_answers = AnswerSetAnswer.objects.filter(answer_set=answer_set.id).count()
if (action == 'pre_remove'):
num_answers -= int(kwargs.get('pk_set'))
elif (action == 'pre_add'):
num_answers += int(kwargs.get('pk_set'))
elif (action == 'clear'):
num_answers = 0
answer_set.num_answers = num_answers
print 'n a: ', answer_set.num_answers
answer_set.save()
m2m_changed.connect(update_answer_set_num_answers, \
AnswerSet.answers.through, weak=False)
Do you really need to denormalise this? You can calculate it with a simple aggregate:
from django.db.models import Count
answersets = AnswerSet.objects.all().annotate(num_answers=Count('answers')