Preserve empty message in ruamel.yaml roundtrip parsing - python-2.7
Using ruamel.yaml, the output of roundtrip parsing of the YAML
a: {b: }
is
a: {b: !!null ''}
Is there any way to preserve the empty message, or overwrite the None representer to output an empty message as above?
This is not trivial and I am not sure if the solution presented below doesn't have adverse side-effects.
The cause for the non-triviality has to do with multiple things:
You are using the less readable flow style
a: {b: }
instead of block style:
a:
b:
The latter round-trips without a change
The empty value for key b loads as None, which I have not been able to subclass in ruamel.yaml, and hence style information cannot be attached to that value, and you have to rely on the "default" emitter (which in your case doesn't do what you want).
This
a: {b:}
is entirely different from your
a: {b: }
and currently the emitter goes for safe and inserts tag information into the stream.
With that background information, you can force the style of the representation for None to the empty string and based on that hack the emitter:
import sys
import ruamel.yaml
yaml_str = """\
a: {b: }
"""
class MyEmitter(ruamel.yaml.emitter.Emitter):
def choose_scalar_style(self):
# override selection for 'null'
if self.analysis is None:
self.analysis = self.analyze_scalar(self.event.value)
if self.event.style == '"' or self.canonical:
return '"'
if (not self.event.style or self.event.style == '?') and \
(self.event.implicit[0] or not self.event.implicit[2]):
if (not (self.simple_key_context and
(self.analysis.empty or self.analysis.multiline)) and
(self.flow_level and self.analysis.allow_flow_plain or
(not self.flow_level and self.analysis.allow_block_plain))):
return ''
if (self.event.style == '') and self.event.tag == 'tag:yaml.org,2002:null' and \
(self.event.implicit[0] or not self.event.implicit[2]):
if self.flow_level and not self.analysis.allow_flow_plain:
return ''
self.analysis.allow_block = True
if self.event.style and self.event.style in '|>':
if (not self.flow_level and not self.simple_key_context and
self.analysis.allow_block):
return self.event.style
if not self.event.style and self.analysis.allow_double_quoted:
if "'" in self.event.value or '\n' in self.event.value:
return '"'
if not self.event.style or self.event.style == '\'':
if (self.analysis.allow_single_quoted and
not (self.simple_key_context and self.analysis.multiline)):
return '\''
return '"'
def process_scalar(self):
# if style '' and tag is 'null' insert empty space
if self.analysis is None:
self.analysis = self.analyze_scalar(self.event.value)
if self.style is None:
self.style = self.choose_scalar_style()
split = (not self.simple_key_context)
if self.sequence_context and not self.flow_level:
self.write_indent()
if self.style == '"':
self.write_double_quoted(self.analysis.scalar, split)
elif self.style == '\'':
self.write_single_quoted(self.analysis.scalar, split)
elif self.style == '>':
self.write_folded(self.analysis.scalar)
elif self.style == '|':
self.write_literal(self.analysis.scalar)
elif self.event.tag == 'tag:yaml.org,2002:null':
self.stream.write(u' ') # not sure if this doesn't break other things
else:
self.write_plain(self.analysis.scalar, split)
self.analysis = None
self.style = None
if self.event.comment:
self.write_post_comment(self.event)
class MyRepresenter(ruamel.yaml.representer.RoundTripRepresenter):
def represent_none(self, data):
if len(self.represented_objects) == 0 and not self.serializer.use_explicit_start:
# this will be open ended (although it is not yet)
return self.represent_scalar(u'tag:yaml.org,2002:null', u'null')
return self.represent_scalar(u'tag:yaml.org,2002:null', u'', style='')
MyRepresenter.add_representer(type(None),
MyRepresenter.represent_none)
yaml = ruamel.yaml.YAML()
yaml.Emitter = MyEmitter
yaml.Representer = MyRepresenter
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)
which gives:
a: {b: }
Related
How to restore sqlite3 database file programmatically in django
The user wishes to have a database backup and restore functionality through the application. They want to be able to download the database file and upload it to restore the database whenever needed. The problem is that django is already running the current DB file. I wrote the following logic to restore the database. folder ='./' if request.method == 'POST': myfile = request.FILES['file'] fs = FileSystemStorage(location=folder) if myfile.name.split(".")[1] != "sqlite3": return JsonResponse({"res":"Please upload a database file."}) if os.path.isfile(os.path.join(folder, "db.sqlite3")): os.remove(os.path.join(folder, "db.sqlite3")) filename = fs.save("db.sqlite3", myfile) file_url = fs.url(filename) return JsonResponse({"res":file_url}) I get the following error which is rightly justified: [WinError 32] The process cannot access the file because it is being used by another process So, is there a way I can achieve this functionality through my application?
I found a better way to create this functionality using a csv. One could store the data from the DB into a csv file and restore it when uploaded. Following is my implementation: def back_up_done(request): tables = getTableNames() sql3_cursor = connection.cursor() for table in tables: sql3_cursor.execute(f'SELECT * FROM {table}') with open('output.csv','a') as out_csv_file: csv_out = csv.writer(out_csv_file) for result in sql3_cursor: csv_out.writerow('Null' if x is None else x for x in result) csv_out.writerow("|") BaseDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) csv_path = os.path.join(BaseDir, 'output.csv') dbfile = File(open(csv_path, "rb")) response = HttpResponse(dbfile, content_type='application/csv') response['Content-Disposition'] = 'attachment; filename=%s' % 'backup.csv' response['Content-Length'] = dbfile.size os.remove(os.path.join("./", "output.csv")) return response def back_up_restore(request): if request.method == 'POST': DBfile = request.FILES['file'] cursor = connection.cursor() if DBfile.name.split(".")[1] != "csv": messages.add_message(request, messages.ERROR, "Please upload a CSV file.") return redirect('back-up-db') tables = getTableNames() i = 0 deleteColumns() decoded_file = DBfile.read().decode('utf-8').splitlines() reader = csv.reader(decoded_file) for row in reader: if len(row) != 0: if(row[0] == "|"): i += 1 else: query = f'''Insert into {tables[i]} values({concatRowValues(row)})''' cursor.execute(query) connection.close() messages.add_message(request, messages.SUCCESS, "Data Restored Successfully.") return redirect('login') def concatRowValues(row): data = '' for i,value in enumerate(row): if value == "False": value = '0' elif value == "True": value = '1' if i != len(row) - 1: data = f'{data}{str(value)},' if value == "Null" else f'{data}\'{str(value)}\',' else: data = f'{data}{str(value)}' if value == "Null" else f'{data}\'{str(value)}\'' return data Where getTableNames and concatRowValues are helper functions to get the names of tables and to concatenate column values to form an executable sql statement, respectively. The "|" character is used to mark the end of a table's data.
How to catch the stack status in Openstack
I have following conditions 1. stackCreate 2. stackUpdate 3. stackCreate What I am trying to do is, while the stackCreate/Update/Delete is triggered, I need to check on the progress. How can I do that? I know of 2 wayts 1. openstack stack event list . 2. I have below python code. stack_id = str(hc.stacks.get(stack_name).id) hc.stacks.delete(stack_id=stack_id) try: evntsdata = hc.events.list(stack_name)[0].to_dict() event_handle = evntsdata['resource_status'] if event_handle == 'DELETE_IN_PROGRESS': loopcontinue = True while loopcontinue: evntsdata = hc.events.list(stack_name)[0].to_dict() event_handle = evntsdata['resource_status'] if event_handle == 'DELETE_COMPLETE': loopcontinue = False print(str(timestamp()) + " " + "Delete is Completed!") elif event_handle == 'DELETE_FAILED': print("Failed") # this needs a proper error msg sys.exit(0) else: print(str(timestamp()) + " " + "Delete in Progress!") time.sleep(5) elif event_handle == 'DELETE_COMPLETE': print(str(timestamp()) + " " + "Delete is Completed!") sys.exit(0) elif event_handle == 'DELETE_FAILED': print("Failed") sys.exit(0) except AttributeError as e: print(str(timestamp()) + " " + "ERROR: Stack Delete Failure") raise except (RuntimeError, heatclient.exc.NotFound): print("Stack doesnt exist:", stack_name) The first method is shell command in which I am not very good. (or lets say I dont know how to best integrate the shell command in python) The problem with both the methods is that I am putting these many steps to identify whether the stack delete is successful. And I am repeating the same for stackupdate and create which is not best practice I am thinking. Anyone has any idea how I can minimize this logic? Any help is greatly appreciated.
You can write simple functions to create/update/delete stack and also to check the status of stack. Please check below sample code to create a stack and poll the status of the stack. from keystoneauth1 import loading from keystoneauth1 import session from heatclient import client tenant_id = 'ab3fd9ca29e149acb25161ec8053da9c' heat_url = 'http://10.26.12.31:8004/v1/%s' % tenant_id auth_token = 'gAAAAABZYxfjz88XNXnfoCPkNLVeVtqtJ9o8qEtgFhI2GJ-ewSCuiypdwt3K5evgQeICVRqMa2jXgzVlENAUB19ZNyQfVCxSX4_lMBKyChM76SGuQUP8U-xJ9EKIfFaVwRGBkk4Ow9OO-iNINfMs0B5-LzJvxTFybi8yZw4EiagQpNpfu1onYfc' heat = client.Client('1', endpoint=heat_url, token=auth_token) def create_stack(stack_file_path, stack_name, parameters=None): template = open(stack_file_path) if parameters: stack = heat.stacks.create(stack_name=stack_name, template=template.read(), parameters=parameters) else: stack = heat.stacks.create(stack_name=stack_name, template=template.read()) template.close() return stack def get_stack_status(stack_id): stack = heat.stacks.get(stack_id) return stack.stack_status def poll_stack_status(stack_id, poll_time=5): stack_status = get_stack_status(stack_id) while stack_status != 'CREATE_COMPLETE': if stack_status == 'CREATE_FAILED': return 1 time.sleep(poll_time) stack_status = get_stack_status(stack_id) return 0
I worked it with below for now. It's not the best I think but satisfies what I need to do. def stackStatus(status): evntsdata = hc.events.list(stack_name)[0].to_dict() event_handle = evntsdata['resource_status'].split("_") event_handle = '_'.join(event_handle[1:]) if event_handle == 'IN_PROGRESS': loopcontinue = True while loopcontinue: evntsdata = hc.events.list(stack_name)[0].to_dict() event_handle = evntsdata['resource_status'].split("_") event_handle = '_'.join(event_handle[1:]) if event_handle == 'COMPLETE': loopcontinue = False print(str(timestamp()) + status + " IS COMPLETED!") elif event_handle == 'FAILED': print("Failed") exit(1) else: print(str(timestamp()) + status + " IN PROGRESS!") time.sleep(5) Call this function stackStatus("DELETE") stackStatus("CREATE") stackStatus("UPDATE")
Replace string before brace in Python-script
I modified jtaubers python script to convert betababel-code to the original greek letters. After some modifications (and help!) I got the script to run. # beta2unicode.py # # Version 2004-11-23 # # James Tauber # http://jtauber.com/ # # You are free to redistribute this, but please inform me of any errors # # USAGE: # # trie = beta2unicodeTrie() # beta = "LO/GOS\n"; # unicode, remainder = trie.convert(beta) # # - to get final sigma, string must end in \n # - remainder will contain rest of beta if not all can be converted class Trie: def __init__(self): self.root = [None, {}] def add(self, key, value): curr_node = self.root for ch in key: curr_node = curr_node[1].setdefault(ch, [None, {}]) curr_node[0] = value def find(self, key): curr_node = self.root for ch in key: try: curr_node = curr_node[1][ch] except KeyError: return None return curr_node[0] def findp(self, key): curr_node = self.root remainder = key for ch in key: try: curr_node = curr_node[1][ch] except KeyError: return (curr_node[0], remainder) remainder = remainder[1:] return (curr_node[0], remainder) def convert(self, keystring): valuestring = "" key = keystring while key: value, key = self.findp(key) if not value: return (valuestring, key) valuestring += value return (valuestring, key) def beta2unicodeTrie(): t = Trie() t.add("*A", u"\u0391") t.add("*B", u"\u0392") t.add("*G", u"\u0393") t.add("*D", u"\u0394") t.add("*E", u"\u0395") t.add("*Z", u"\u0396") t.add("*H", u"\u0397") t.add("*Q", u"\u0398") t.add("*I", u"\u0399") t.add("*K", u"\u039A") t.add("*L", u"\u039B") t.add("*M", u"\u039C") t.add("*N", u"\u039D") t.add("*C", u"\u039E") t.add("*O", u"\u039F") t.add("*P", u"\u03A0") t.add("*R", u"\u03A1") t.add("*S", u"\u03A3") t.add("*T", u"\u03A4") t.add("*U", u"\u03A5") t.add("*F", u"\u03A6") t.add("*X", u"\u03A7") t.add("*Y", u"\u03A8") t.add("*W", u"\u03A9") t.add("A", u"\u03B1") t.add("B", u"\u03B2") t.add("G", u"\u03B3") t.add("D", u"\u03B4") t.add("E", u"\u03B5") t.add("Z", u"\u03B6") t.add("H", u"\u03B7") t.add("Q", u"\u03B8") t.add("I", u"\u03B9") t.add("K", u"\u03BA") t.add("L", u"\u03BB") t.add("M", u"\u03BC") t.add("N", u"\u03BD") t.add("C", u"\u03BE") t.add("O", u"\u03BF") t.add("P", u"\u03C0") t.add("R", u"\u03C1") t.add("S\n", u"\u03C2") t.add("S,", u"\u03C2,") t.add("S.", u"\u03C2.") t.add("S:", u"\u03C2:") t.add("S}", u"\u03C2:") t.add("S;", u"\u03C2;") t.add("S]", u"\u03C2]") t.add("S#", u"\u03C2#") t.add("S_", u"\u03C2_") t.add("S", u"\u03C3") t.add("T", u"\u03C4") t.add("U", u"\u03C5") t.add("F", u"\u03C6") t.add("X", u"\u03C7") t.add("Y", u"\u03C8") t.add("W", u"\u03C9") t.add("I+", U"\u03CA") t.add("U+", U"\u03CB") t.add("A)", u"\u1F00") t.add("A(", u"\u1F01") t.add("A)\\", u"\u1F02") t.add("A(\\", u"\u1F03") t.add("A)!", u"\u1F02") t.add("A(!", u"\u1F03") t.add("A)/", u"\u1F04") t.add("A(/", u"\u1F05") t.add("E)", u"\u1F10") t.add("E(", u"\u1F11") t.add("E)\\", u"\u1F12") t.add("E(\\", u"\u1F13") t.add("E)!", u"\u1F12") t.add("E(!", u"\u1F13") t.add("E)/", u"\u1F14") t.add("E(/", u"\u1F15") t.add("H)", u"\u1F20") t.add("H(", u"\u1F21") t.add("H)\\", u"\u1F22") t.add("H(\\", u"\u1F23") t.add("H)!", u"\u1F22") t.add("H(!", u"\u1F23") t.add("H)/", u"\u1F24") t.add("H(/", u"\u1F25") t.add("I)", u"\u1F30") t.add("I(", u"\u1F31") t.add("I)\\", u"\u1F32") t.add("I(\\", u"\u1F33") t.add("I)!", u"\u1F32") t.add("I(!", u"\u1F33") t.add("I)/", u"\u1F34") t.add("I(/", u"\u1F35") t.add("O)", u"\u1F40") t.add("O(", u"\u1F41") t.add("O)\\", u"\u1F42") t.add("O(\\", u"\u1F43") t.add("O)!", u"\u1F42") t.add("O(!", u"\u1F43") t.add("O)/", u"\u1F44") t.add("O(/", u"\u1F45") t.add("U)", u"\u1F50") t.add("U(", u"\u1F51") t.add("U)\\", u"\u1F52") t.add("U(\\", u"\u1F53") t.add("U)!", u"\u1F52") t.add("U(!", u"\u1F53") t.add("U)/", u"\u1F54") t.add("U(/", u"\u1F55") t.add("W)", u"\u1F60") t.add("W(", u"\u1F61") t.add("W)\\", u"\u1F62") t.add("W(\\", u"\u1F63") t.add("W)!", u"\u1F62") t.add("W(!", u"\u1F63") t.add("W)/", u"\u1F64") t.add("W(/", u"\u1F65") t.add("A)=", u"\u1F06") t.add("A(=", u"\u1F07") t.add("H)=", u"\u1F26") t.add("H(=", u"\u1F27") t.add("I)=", u"\u1F36") t.add("I(=", u"\u1F37") t.add("U)=", u"\u1F56") t.add("U(=", u"\u1F57") t.add("W)=", u"\u1F66") t.add("W(=", u"\u1F67") t.add("*A)", u"\u1F08") t.add("*)A", u"\u1F08") t.add("*A(", u"\u1F09") t.add("*(A", u"\u1F09") # t.add("*(\A", u"\u1F0B") t.add("*A)/", u"\u1F0C") t.add("*)/A", u"\u1F0C") t.add("*A(/", u"\u1F0F") t.add("*(/A", u"\u1F0F") t.add("*E)", u"\u1F18") t.add("*)E", u"\u1F18") t.add("*E(", u"\u1F19") t.add("*(E", u"\u1F19") # t.add("*(\E", u"\u1F1B") t.add("*E)/", u"\u1F1C") t.add("*)/E", u"\u1F1C") t.add("*E(/", u"\u1F1D") t.add("*(/E", u"\u1F1D") t.add("*H)", u"\u1F28") t.add("*)H", u"\u1F28") t.add("*H(", u"\u1F29") t.add("*(H", u"\u1F29") t.add("*H)\\", u"\u1F2A") t.add(")\\*H", u"\u1F2A") t.add("*)\\H", u"\u1F2A") t.add("*H)!", u"\u1F2A") t.add(")!*H", u"\u1F2A") t.add("*)!H", u"\u1F2A") # t.add("*H)/", u"\u1F2C") t.add("*)/H", u"\u1F2C") # t.add("*)=H", u"\u1F2E") t.add("(/*H", u"\u1F2F") t.add("*(/H", u"\u1F2F") t.add("*I)", u"\u1F38") t.add("*)I", u"\u1F38") t.add("*I(", u"\u1F39") t.add("*(I", u"\u1F39") # # t.add("*I)/", u"\u1F3C") t.add("*)/I", u"\u1F3C") # # t.add("*I(/", u"\u1F3F") t.add("*(/I", u"\u1F3F") # t.add("*O)", u"\u1F48") t.add("*)O", u"\u1F48") t.add("*O(", u"\u1F49") t.add("*(O", u"\u1F49") # # t.add("*(\O", u"\u1F4B") t.add("*O)/", u"\u1F4C") t.add("*)/O", u"\u1F4C") t.add("*O(/", u"\u1F4F") t.add("*(/O", u"\u1F4F") # t.add("*U(", u"\u1F59") t.add("*(U", u"\u1F59") # t.add("*(/U", u"\u1F5D") # t.add("*(=U", u"\u1F5F") t.add("*W)", u"\u1F68") t.add("*W(", u"\u1F69") t.add("*(W", u"\u1F69") # # t.add("*W)/", u"\u1F6C") t.add("*)/W", u"\u1F6C") t.add("*W(/", u"\u1F6F") t.add("*(/W", u"\u1F6F") t.add("*A)=", u"\u1F0E") t.add("*)=A", u"\u1F0E") t.add("*A(=", u"\u1F0F") t.add("*W)=", u"\u1F6E") t.add("*)=W", u"\u1F6E") t.add("*W(=", u"\u1F6F") t.add("*(=W", u"\u1F6F") t.add("A\\", u"\u1F70") t.add("A!", u"\u1F70") t.add("A/", u"\u1F71") t.add("E\\", u"\u1F72") t.add("E!", u"\u1F72") t.add("E/", u"\u1F73") t.add("H\\", u"\u1F74") t.add("H!", u"\u1F74") t.add("H/", u"\u1F75") t.add("I\\", u"\u1F76") t.add("I!", u"\u1F76") t.add("I/", u"\u1F77") t.add("O\\", u"\u1F78") t.add("O!", u"\u1F78") t.add("O/", u"\u1F79") t.add("U\\", u"\u1F7A") t.add("U!", u"\u1F7A") t.add("U/", u"\u1F7B") t.add("W\\", u"\u1F7C") t.add("W!", u"\u1F7C") t.add("W/", u"\u1F7D") t.add("A)/|", u"\u1F84") t.add("A(/|", u"\u1F85") t.add("H)|", u"\u1F90") t.add("H(|", u"\u1F91") t.add("H)/|", u"\u1F94") t.add("H)=|", u"\u1F96") t.add("H(=|", u"\u1F97") t.add("W)|", u"\u1FA0") t.add("W(=|", u"\u1FA7") t.add("A=", u"\u1FB6") t.add("H=", u"\u1FC6") t.add("I=", u"\u1FD6") t.add("U=", u"\u1FE6") t.add("W=", u"\u1FF6") t.add("I\\+", u"\u1FD2") t.add("I!+", u"\u1FD2") t.add("I/+", u"\u1FD3") t.add("I+/", u"\u1FD3") t.add("U\\+", u"\u1FE2") t.add("U!+", u"\u1FE2") t.add("U/+", u"\u1FE3") t.add("A|", u"\u1FB3") t.add("A/|", u"\u1FB4") t.add("H|", u"\u1FC3") t.add("H/|", u"\u1FC4") t.add("W|", u"\u1FF3") t.add("W|/", u"\u1FF4") t.add("W/|", u"\u1FF4") t.add("A=|", u"\u1FB7") t.add("H=|", u"\u1FC7") t.add("W=|", u"\u1FF7") t.add("R(", u"\u1FE4") t.add("*R(", u"\u1FEC") t.add("*(R", u"\u1FEC") # t.add("~", u"~") # t.add("-", u"-") # t.add("(null)", u"(null)") # t.add("&", "&") t.add("0", u"0") t.add("1", u"1") t.add("2", u"2") t.add("3", u"3") t.add("4", u"4") t.add("5", u"5") t.add("6", u"6") t.add("7", u"7") t.add("8", u"8") t.add("9", u"9") t.add("#", u"#") t.add("$", u"$") t.add(" ", u" ") t.add(".", u".") t.add(",", u",") t.add("'", u"'") t.add(":", u":") t.add(";", u";") t.add("_", u"_") t.add("[", u"[") t.add("]", u"]") t.add("\n", u"") return t import sys t = beta2unicodeTrie() import re BCODE = re.compile(r'\\bcode{[^}]*}') for line in open(sys.argv[1]): matches = BCODE.search(line) for match in BCODE.findall(line): bcode = match[7:-1] a, b = t.convert(bcode.upper()) if b: raise IOError("failed conversion '%s' in '%s'" % (b, line)) converted = a.encode("utf-8") line = line.replace(match, converted) print(line.rstrip()) There is one thing left though. The final sigma "ς" gets not converted like this when it stands at the end of the \bcode{}-Makro. For example here: \bcode{ei)=dos} The script converts it to the normal sigma "σ" Unicode: U+03C3 How do I make the script recognize to convert the "s" when it stands right before the end-brace to the Unicode U+03C2? This: t.add("S}", u"\u03C2}") does not do the trick
Allowing TABs in Char Validator
Currently I have a data input screen where the user has to navigate between input fields using the mouse. I would like to add a TAB facility so that they can TAB between input fields, e.g. Last Name → First Name → DOB, etc. The input screen makes use of Validators to prevent users inputting nonsense:- def OnChar(self, evt): key = chr(evt.GetKeyCode()) if ord(key) in [wx.WXK_BACK, wx.WXK_DELETE]: evt.Skip() if self.flag == LETTERS and key not in string.letters: return if self.flag == DECIMAL_DIGITS and key not in string.digits: return if self.flag == HEX_DIGITS and key not in string.hexdigits: return if self.flag == ALPHA_NUMERIC and key not in string.printable: return evt.Skip() I have added:- style=wx.TAB_TRAVERSAL | wx.DEFAULT_DIALOG_STYLE to the Frame. However the Validator traps the TABs. Is there a way to get around this problem?
Because you are converting the key to chr() it is not recognising wx.WXK_TAB see the following to demonstrate your problem: #!/usr/bin/python # -*- coding: utf-8 -*- import wx class Example(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, wx.ID_ANY, "Tab Key Test",size=(310,120)) self.Stop_prog = False self.top = wx.Panel(self, wx.ID_ANY) self.sizer = wx.BoxSizer(wx.VERTICAL) self.text_input = wx.TextCtrl(self.top, wx.ID_ANY, size=(300,25)) self.sizer.Add(self.text_input) self.text_input.Bind(wx.EVT_KEY_DOWN, self.OnKey) self.top.Sizer = self.sizer self.top.Sizer.Fit(self.top) self.Show() def OnKey(self,event): print "Key Pressed", event.GetKeyCode() keycode = event.GetKeyCode() try: charkey = chr(event.GetKeyCode()) print "Character Key", charkey except: print "unable to convert key to a character in the range 256" if keycode == wx.WXK_F1: self.Close() elif keycode == wx.WXK_TAB: print "TAB" self.text_input.SetValue("You pressed Tab") else: event.Skip() def main(): ex = wx.App() Example(None) ex.MainLoop() if __name__ == '__main__': main()
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')