is there a way I can create a function to prevent my unique key values from changing, or raise an error if there is an attempt to change unique keys values? i am writing an update function for a database to update entries, but i want to make it so if the "unique key values " are in the payload to be updated it will raise an error
mydict = {
'name': 'cameron', # natural keys
'age': '29', #also natural key
'city': 'boston',
}
CodePudding user response:
A simple example will be as follows:
class MyDict(dict):
NATURAL_KEYS = ["name", "age"]
def __setitem__(self, key, value):
if key in self.NATURAL_KEYS:
raise KeyError(f"My natural key `{key}`'s value is prevented from changing.")
super().__setitem__(key, value)
You can read more info here: https://realpython.com/inherit-python-dict/#creating-dictionary-like-classes-in-python.
CodePudding user response:
Looking at existing database designs, they typically only allow updating entire database entries, the dict in your case, rather than only a part of the entry.
An update function would typically look like:
natural_keys = ['name', 'age']
database: Dict[tuple, dict]: ...
def update(new_entry: dict):
keys = tuple(new_entry[k] for k in natural_keys)
if keys in database: # update existing entries
database[keys] = new_entry
else: # raise error if there are no entries with these natural keys
raise ValueError(f"there are no entries with natural keys {keys}")
This way you will never really change the natural keys, only updating existing entries with the same natural keys.
Of course, you will need an insert
function to insert new entries with new natural keys.
So there is never really the problem of preventing user from changing natural keys.
Alternatively
For detailed controls, you can create a class and control property setting via property setter
For example,
class MyDict:
def __init__(self, name, age, city):
self._name = name
self._age = age
self.city = city
@property
def name(self):
return self._name
@name.setter
def name(self, value):
raise ValueError("can't set name")
@property
def age(self):
return self._age
@age.setter
def age(self, value):
raise ValueError("can't set age")
So you can define
my_dict = MyDict(name='cameron', age=29, city='boston')
You can set city
my_dict.city = 'new york'
But you will get ValueError
when you try to set name
and age
.
In more advanced usage, you can use descriptors to handle general attribute setting
import logging
class PreventSetting:
def __set_name__(self, owner, name):
self.public_name = name
self.private_name = '_' name
def __get__(self, obj, objtype=None):
value = getattr(obj, self.private_name)
logging.info('Accessing %r giving %r', self.public_name, value)
return value
def __set__(self, obj, value):
# if defining for 1st time
if not hasattr(obj, self.private_name):
logging.info('Updating %r to %r', self.public_name, value)
setattr(obj, self.private_name, value)
else:
# raise error for setting value after 1st time
raise ValueError(f"can't set {self.public_name}")
class MyDict:
name = PreventSetting() # First descriptor instance
age = PreventSetting() # Second descriptor instance
def __init__(self, name, age, city):
self.name = name # Calls the first descriptor __set__
self.age = age # Calls the second descriptor __set__
self.city = city
CodePudding user response:
Poorly formulated question. What is natural key?
Anyways, if you want to restrict the values of the keys, you can compute whether the new value is valid (by an algorithm that deal with natural keys) and then throw exceptions with raise ValueError()
statement.
CodePudding user response:
def verify(new, dc: dictionary):
"""Verifies if the passed value is present in the dictionary keys."""
return False if new in dc.keys else True