Datastore set property name from variable - python

I have an unknown variable that I want to use as a datastore property name. I'm using Expando, as I know you can dynamically create properties without first declaring them in the db class, however I am unable to do this as the property names are not known. I get the error: 'StoreNames' object does not support item assignment. Is there any way around this?
class StoreNames(db.Expando):
index = db.FloatProperty()
name = "unknown"
value = "something"
store = StoreNames()
store[name] = value
store.index = 0

Solved by using the following code:
class StoreNames(db.Expando):
index = db.FloatProperty()
name = "unknown"
value = "something"
store = StoreNames()
setattr(db, name, value)
I would have answered earlier but Stackoverflow wouldn't let me. Thanks Brent Washburne

Related

How do I select a row in sqlite with a name on the row instead of an id with python?

I'm programming a store management system in python using sqlite as database. I want to be able to see Item price when the name of the item is entered instead of the Item id...below is the code snippet
def ajax2(self, *args, **kwargs):
self.get_name = self.entername_e.get()
#get the products info with that name or id and fill labels above
query = "SELECT * FROM inventory WHERE name=?"
result = c.execute(query, (self.get_name, ))
for self.r in result:
self.get_name = self.r[1] #name
self.get_price = self.r[4] #sp
self.get_stock = self.r[2] #stock
self.productname.configure(text="Product's Name: "+ str(self.get_name))
self.pprice.configure(text="Price: Gh "+str(self.get_price))
Anytime I run the code after entering the name in the label Entry, the name appears even if it's not in the database and an error message follows in the command line like below:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\GH\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in
__call__
return self.func(*args)
File "main.py", line 96, in ajax2
self.pprice.configure(text="Price: Gh "+str(self.get_price))
AttributeError: 'Application' object has no attribute 'get_price'
PS C:\Users\GH\Desktop\Developments\Store Management Software>
If the name is not present in the database, the cursor returned by the query is empty and the loop is not entered. As a result, self.get_name is unchanged and keeps the entered value, and self.get_price is not set, hence the error.
You shoud explicitely test for that case:
...
result = c.execute(query, (self.get_name, ))
empty = True
for self.r in result:
empty = False
self.get_name = self.r[1] #name
self.get_price = self.r[4] #sp
self.get_stock = self.r[2] #stock
if empty: // name is not present in database
...
I don't think that you understand the object oriented approach in this python code. The self keyword is used to access the underlying object, of which all the attributes should be defined in the init function of the class. These attributes should only be there to represent the state of an instantation of the class. See this link : link
You should always be careful when altering objects by accessing the attributes directly. A common practice is to only access them by getters and setters, e.g.
def getAttribute_1(self):
return self.attribute_1
There are lots of good books and tutorials about object oriented programming available if you want to learn more about it, just google it.
In this case, for example, I don't see why you do:
for self.r in result: ...
when you should let go off the self keyword:
for r in result: ...
As this result variable is only relevant within the scope of your function.
Furthermore, if you want to set the price attribute of your current object, you should define that attribute in the init function:
def __init__(self, ...)
self.price = 0 """or whatever default value
you want to give it, or initiate it by giving the initial
value as an argument for the constructor"""
maybe define a getter and setter like this:
def get_price(self):
return self.price
def set_price(self, new_price):
self.price = new_price
Then use these methods in your ajax function. Just remember to make a distinction between local variables, e.g. price = result[4] which are only relevant in this function, and attributes of your object, like self.price! These are 2 very different concepts.
To respond to your error in this example: the get_price is not set if the results returned by the query are None, in which case the loop will not be entered. In this case, the self.get_price attribute of you object has never been created, hence the error.
Hope this helps

Class Variable NameError not defined python

In this example, it's working
hotels as a class variable no NameError
class Hotel():
"""""""""
this is hotel class file
"""
hotels = []
def __init__(self,number,hotel_name,city,total_number,empty_rooms):
self.number = number
self.hotel_name = hotel_name
self.city = city
self.total_number = total_number
self.empty_rooms = empty_rooms
Hotel.hotels.append([number,hotel_name,city,total_number,empty_rooms])
def list_hotels_in_city(self,city):
for i in hotels:
if city in i:
print "In ",city,": ",i[1],"hotel, available rooms :",i[4]
In the following example its not working
from twilio.rest import Client
class Notifications():
customers = []
def __init__(self,customer_name,number,message):
self.customer_name = customer_name
self.number = number
self.message = message
Notifications.customers.append([customer_name,number,message])
def send_text_message(self,customer_name):
for i in customers:
print "triggeredb"
inst = Notifications("ahmed","+00000000000","messagesample")
print "instance : ",inst.customers
inst.send_text_message("ahmed")
NameError: global name 'customers' is not defined
Update
for first example nothing called to show error
but issue solved for second example Thanks Tom Dalton , scharette and James
As I said in my comment, When you call for i in customers:, customers is not in scope of that function.
I just wanted to add also, that you use
Notifications.customers.append([customer_name,number,message])
but you also declare
customers = []
Note that the former is a class variable and will share the variable among Notifications instances. The latter represent an instance variable. If your goal is to have a customers list for every specific object, you should use self.customers.
So basically,
You want shared list between objects ?
for i in Notifications.customers:
You want specific list for every object ?
for i in self.customers:
I think it's very likely that when you ran your first example, you had a variable called hotels in your global (interpreter) scope. Thats why it's working. If I copy paste your example into my interpreter it fails with the same error message as your second code sample.
If your send_text_message function only accesses class variables (no instance variables) I would recommend making it a class method like this :
#classmethod
def send_text_message(cls, customer_name):
for i in cls.customers:
print "triggeredb"
That way you can access the class variables using the cls variable and won't have to repeat the class name in your function (which is nice, as if you change the class name - you won't have to go hunting through your code for repetitions).

Defining circular references using zope.schema

I'm trying to do the following, define two classes whose instances mutually reference one another, like Users and Groups in the following exemple. A User can belong to several groups and a Group can contains several users. The actual data is stored in a database and there it is a simple matter of many-to-many relationship using foreign keys. No problem at all.
Afterward the data is loaded through an ORM and stored in instances of python objects. Still no problem at all as the ORM used (SQLAlchemy) manage backrefs.
Now I want to check that the python objects comply to some interface using zope.interface and zope.schema. That's where I get into troubles.
import zope.schema as schema
from zope.interface import Interface, implements
class IGroup(Interface):
name = schema.TextLine(title=u"Group's name")
# user_list = schema.List(title = u"List of Users in this group", value_type = sz.Object(IUser))
class IUser(Interface):
name = schema.TextLine(title=u"User's name")
group_list = schema.List(title = u"List of Groups containing that user",
value_type = schema.Object(IGroup))
IGroup._InterfaceClass__attrs['user_list'] = zs.List(title = u"List of Users in this group", required = False, value_type = zs.Object(IUser))
class Group(object):
implements(IGroup)
def __init__(self, name):
self.name = name
self.user_list = []
class User(object):
implements(IUser)
def __init__(self, name):
self.name = name
self.group_list = []
alice = User(u'Alice')
bob = User(u'Bob')
chuck = User(u'Chuck')
group_users = Group(u"Users")
group_auditors = Group(u"Auditors")
group_administrators = Group(u"Administrators")
def add_user_in_group(user, group):
user.group_list.append(group)
group.user_list.append(user)
add_user_in_group(alice, group_users)
add_user_in_group(bob, group_users)
add_user_in_group(chuck, group_users)
add_user_in_group(chuck, group_auditors)
add_user_in_group(chuck, group_administrators)
for x in [alice, bob, chuck]:
errors = schema.getValidationErrors(IUser, x)
if errors: print errors
print "User ", x.name, " is in groups ", [y.name for y in x.group_list]
for x in [group_users, group_auditors, group_administrators]:
errors = schema.getValidationErrors(IGroup, x)
if errors: print errors
print "Group ", x.name, " contains users ", [y.name for y in x.user_list]
My problem is the commented line. I can't define IGroup using IUser because at that time IUser is not yet defined. I've found a workaround completing the definition of IGroup after the definition of IUser but that is not satisfying at all, because IUser and IGroup are defined in different source files and part of IGroup is defined in the file defining IUser.
Is there any proper way to do that using zope.schema ?
Modify the field after definition:
#imports elided
class IFoo(Interface):
bar = schema.Object(schema=Interface)
class IBar(Interface):
foo = schema.Object(schema=IFoo)
IFoo['bar'].schema = IBar
Martijn's answer seems a bit more graceful and self-documenting, but this is a bit more succinct. Neither is perfect (compared to say, Django's solution of using string names for foreign keys) -- pick your poison.
IMHO, it would be nice to specify a dotted name to an interface instead of an identifier. You could pretty easily create a subclass of schema.Object to this end for your own use, should you find that approach useful.
You could define a base, or abstract, interface for IUser:
class IAbstractUser(Interface):
name = schema.TextLine(title=u"User's name")
class IGroup(Interface):
name = schema.TextLine(title=u"Group's name")
user_list = schema.List(
title=u"List of Users in this group",
value_type=schema.Object(IAbstractUser))
class IUser(IAbstractUser):
group_list = schema.List(
title=u"List of Groups containing that user",
value_type=schema.Object(IGroup))
Because IUser is a subclass of IAbstractUser, objects implementing the former also satisfy the latter interface.
Edit: You can always still apply sdupton's dynamic after-the-fact alteration of the IGroup interface after you defined IUser:
IGroup['user_list'].value_type.schema = IUser
I'd still use the Abstract interface pattern to facilitate better code documentation.

how to get entities which don't have certain attribute in datastore

I'm trying to make an appraisal system
This is my class
class Goal(db.Expando):
GID = db.IntegerProperty(required=True)
description = db.TextProperty(required=True)
time = db.FloatProperty(required=True)
weight = db.IntegerProperty(required=True)
Emp = db.UserProperty(auto_current_user=True)
Status = db.BooleanProperty(default=False)
Following things are given by employee,
class SubmitGoal(webapp.RequestHandler):
def post(self):
dtw = simplejson.loads(self.request.body)
try:
maxid = Goal.all().order("-GID").get().GID + 1
except:
maxid = 1
try:
g = Goal(GID=maxid, description=dtw[0], time=float(dtw[1]), weight=int(dtw[2]))
g.put()
self.response.out.write(simplejson.dumps("Submitted"))
except:
self.response.out.write(simplejson.dumps("Error"))
Now, here Manager checks the goals and approve it or not.. if approved then status will be stored as true in datastore else false
idsta = simplejson.loads(self.request.body)
try:
g = db.Query(Goal).filter("GID =", int(idsta[0])).get()
if g:
if idsta[1]:
g.Status=True
try:
del g.Comments
except:
None
else:
g.Status=False
g.Comments=idsta[2]
db.put(g)
self.response.out.write(simplejson.dumps("Submitted"))
except:
self.response.out.write(simplejson.dumps("Error"))
Now, this is where im stuck..."filter('status=',True)".. this is returning all the entities which has status true.. means which are approved.. i want those entities which are approved AND which have not been assessed by employee yet..
def get(self):
t = []
for g in Goal.all().filter("Status = ",True):
t.append([g.GID, g.description, g.time, g.weight, g.Emp])
self.response.out.write(simplejson.dumps(t))
def post(self):
idasm = simplejson.loads(self.request.body)
try:
g = db.Query(Goal).filter("GID =", int(idasm[0])).get()
if g:
g.AsmEmp=idasm[1]
db.put(g)
self.response.out.write(simplejson.dumps("Submitted"))
except:
self.response.out.write(simplejson.dumps("Error"))
How am I supposed to do this? as I know that if I add another filter like "filter('AsmEmp =', not None)" this will only return those entities which have the AsmEmp attribute what I need is vice versa.
You explicitly can't do this. As the documentation states:
It is not possible to query for entities that are missing a given property.
Instead, create a property for is_assessed which defaults to False, and query on that.
could you not simply add another field for when employee_assessed = db.user...
and only populate this at the time when it is assessed?
The records do not lack the attribute in the datastore, it's simply set to None. You can query for those records with Goal.all().filter('status =', True).filter('AsmEmp =', None).
A few incidental suggestions about your code:
'Status' is a rather unintuitive name for a boolean.
It's generally good Python style to begin properties and attributes with a lower-case letter.
You shouldn't iterate over a query directly. This fetches results in batches, and is much less efficient than doing an explicit fetch. Instead, fetch the number of results you need with .fetch(n).
A try/except with no exception class specified and no action taken when an exception occurs is a very bad idea, and can mask a wide variety of issues.
Edit: I didn't notice that you were using an Expando - in which case #Daniel's answer is correct. There doesn't seem to be any good reason to use Expando here, though. Adding the property to the model (and updating existing entities) would be the easiest solution here.

Copy an entity in Google App Engine datastore in Python without knowing property names at 'compile' time

In a Python Google App Engine app I'm writing, I have an entity stored in the datastore that I need to retrieve, make an exact copy of it (with the exception of the key), and then put this entity back in.
How should I do this? In particular, are there any caveats or tricks I need to be aware of when doing this so that I get a copy of the sort I expect and not something else.
ETA: Well, I tried it out and I did run into problems. I would like to make my copy in such a way that I don't have to know the names of the properties when I write the code. My thinking was to do this:
#theThing = a particular entity we pull from the datastore with model Thing
copyThing = Thing(user = user)
for thingProperty in theThing.properties():
copyThing.__setattr__(thingProperty[0], thingProperty[1])
This executes without any errors... until I try to pull copyThing from the datastore, at which point I discover that all of the properties are set to None (with the exception of the user and key, obviously). So clearly this code is doing something, since it's replacing the defaults with None (all of the properties have a default value set), but not at all what I want. Suggestions?
Here you go:
def clone_entity(e, **extra_args):
"""Clones an entity, adding or overriding constructor attributes.
The cloned entity will have exactly the same property values as the original
entity, except where overridden. By default it will have no parent entity or
key name, unless supplied.
Args:
e: The entity to clone
extra_args: Keyword arguments to override from the cloned entity and pass
to the constructor.
Returns:
A cloned, possibly modified, copy of entity e.
"""
klass = e.__class__
props = dict((k, v.__get__(e, klass)) for k, v in klass.properties().iteritems())
props.update(extra_args)
return klass(**props)
Example usage:
b = clone_entity(a)
c = clone_entity(a, key_name='foo')
d = clone_entity(a, parent=a.key().parent())
EDIT: Changes if using NDB
Combining Gus' comment below with a fix for properties that specify a different datastore name, the following code works for NDB:
def clone_entity(e, **extra_args):
klass = e.__class__
props = dict((v._code_name, v.__get__(e, klass)) for v in klass._properties.itervalues() if type(v) is not ndb.ComputedProperty)
props.update(extra_args)
return klass(**props)
Example usage (note key_name becomes id in NDB):
b = clone_entity(a, id='new_id_here')
Side note: see the use of _code_name to get the Python-friendly property name. Without this, a property like name = ndb.StringProperty('n') would cause the model constructor to raise an AttributeError: type object 'foo' has no attribute 'n'.
If you're using the NDB you can simply copy with:
new_entity.populate(**old_entity.to_dict())
This is just an extension to Nick Johnson's excellent code to address the problems highlighted by Amir in the comments:
The db.Key value of the ReferenceProperty is no longer retrieved via an unnecessary roundtrip to the datastore.
You can now specify whether you want to skip DateTime properties with the auto_now and/or auto_now_add flag.
Here's the updated code:
def clone_entity(e, skip_auto_now=False, skip_auto_now_add=False, **extra_args):
"""Clones an entity, adding or overriding constructor attributes.
The cloned entity will have exactly the same property values as the original
entity, except where overridden. By default it will have no parent entity or
key name, unless supplied.
Args:
e: The entity to clone
skip_auto_now: If True then all DateTimeProperty propertes will be skipped which have the 'auto_now' flag set to True
skip_auto_now_add: If True then all DateTimeProperty propertes will be skipped which have the 'auto_now_add' flag set to True
extra_args: Keyword arguments to override from the cloned entity and pass
to the constructor.
Returns:
A cloned, possibly modified, copy of entity e.
"""
klass = e.__class__
props = {}
for k, v in klass.properties().iteritems():
if not (type(v) == db.DateTimeProperty and ((skip_auto_now and getattr(v, 'auto_now')) or (skip_auto_now_add and getattr(v, 'auto_now_add')))):
if type(v) == db.ReferenceProperty:
value = getattr(klass, k).get_value_for_datastore(e)
else:
value = v.__get__(e, klass)
props[k] = value
props.update(extra_args)
return klass(**props)
The first if expression is not very elegant so I appreciate if you can share a better way to write it.
I'm neither Python nor AppEngine guru, but couldn't one dynamically get/set the properties?
props = {}
for p in Thing.properties():
props[p] = getattr(old_thing, p)
new_thing = Thing(**props).put()
A variation inspired in Nick's answer which handles the case in which your entity has a (repeated) StructuredProperty, where the StructuredProperty itself has ComputedProperties. It can probably be written more tersely with dict comprehension somehow, but here is the longer version that worked for me:
def removeComputedProps(klass,oldDicc):
dicc = {}
for key,propertType in klass._properties.iteritems():
if type(propertType) is ndb.StructuredProperty:
purged = []
for item in oldDicc[key]:
purged.append(removeComputedProps(propertType._modelclass,item))
dicc[key]=purged
else:
if type(propertType) is not ndb.ComputedProperty:
dicc[key] = oldDicc[key]
return dicc
def cloneEntity(entity):
oldDicc = entity.to_dict()
klass = entity.__class__
dicc = removeComputedProps(klass,oldDicc)
return klass(**dicc)
This can be tricky if you've renamed the underlying keys for your properties... which some people opt to do instead of making mass data changes
say you started with this:
class Person(ndb.Model):
fname = ndb.StringProperty()
lname = ndb.StringProperty()
then one day you really decided that it would be nicer to use first_name and last_name instead... so you do this:
class Person(ndb.Model):
first_name = ndb.StringProperty(name="fname")
last_name = ndb.StringProperty(name="lname")
now when you do Person._properties (or .properties() or person_instance._properties) you will get a dictionary with keys that match the underlying names (fname and lname)... but won't match the actual property names on the class... so it won't work if you put them into the constructor of a new instance, or use the .populate() method (the above examples will break)
In NDB anyways, instances of models have ._values dictionary which is keyed by the underlying property names... and you can update it directly. I ended up with something like this:
def clone(entity, **extra_args):
klass = entity.__class__
clone = klass(**extra_args)
original_values = dict((k,v) for k,v in entity._values.iteritems() if k not in clone._values)
clone._values.update(original_values)
return clone
This isn't really the safest way... as there are other private helper methods that do more work (like validation and conversion of computed properties by using _store_value() and _retrieve_value())... but if you're models are simple enough, and you like living on the edge :)
Here's the code provided by #zengabor with the if expression formatted for easier reading. It may not be PEP-8 compliant:
klass = e.__class__
props = {}
for k, v in klass.properties().iteritems():
if not (type(v) == db.DateTimeProperty and ((
skip_auto_now and getattr(v, 'auto_now' )) or (
skip_auto_now_add and getattr(v, 'auto_now_add')))):
if type(v) == db.ReferenceProperty:
value = getattr(klass, k).get_value_for_datastore(e)
else:
value = v.__get__(e, klass)
props[k] = value
props.update(extra_args)
return klass(**props)

Categories