ndb get & get_or_insert how to use ? (alway raise Exception) - google-app-engine

I write code as below
from google.appengine.ext import ndb
__metaclass__ = type
class UserSession(ndb.Model):
session = ndb.BlobProperty()
class KV:
#staticmethod
def get(id):
r = ndb.Key(UserSession, int(id)).get()
if r:
return r.session
#staticmethod
def set(id, value):
return UserSession.get_or_insert(int(id), session=value)
#staticmethod
def delete(id):
ndb.Key(UserSession, int(id)).delete()
where I write
id = 1
key = ndb.Key(UserSession, int(id))
UserSession.get_or_insert(key, session=1)
the sdk raise
TypeError: name must be a string; received Key('UserSession', 1)
when I call KV.get ()
the sdk raise
File "/home/bitcoin/42btc/zapp/_plugin/auth/model/gae/user.py", line 14, in get
r = ndb.Key(UserSession,int(id)).get()
...
BadRequestError: missing key id/name
So , how to use NDB?

The get_or_insert() method takes a string which is only the ID part of the key, not a Key. It cannot use numeric IDs.

Related

How to create an object attribute without writing to database -- peewee -- python

Maybe i have an understanding problem. I try to make 2 tabeles in one database. But additionaly i need to have some temporary values in one class that i doen´t want to write to the database.
I try to switch to peewee and read the dokumentation but i find no solution at my own.
without peewee i would make an init method where i write my attributes. But where did i have to write them now?
from peewee import *
import datetime
db = SqliteDatabase('test.db', pragmas={'foreign_keys': 1})
class BaseModel(Model):
class Meta:
database = db
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
#def __init__(self):
#self.sometemporaryvariable = "blabla"
def meineparameter(self, hui):
self.hui = hui
print(self.hui)
class Sensor_measure(BaseModel):
id = ForeignKeyField(Sensor, backref="sensorvalues")
timestamp = DateTimeField(default=datetime.datetime.now)
value = FloatField()
class Meta:
primary_key = CompositeKey("id", "timestamp")
db.connect()
db.create_tables([Sensor_measure, Sensor])
sensor1 = Sensor.create(id=2, sort=20, name="Sensor2")
#sensor1.sometemporaryvariable = "not so important to write to the database"
sensor1.save()
Remember to call super() whenever overriding a method in a subclass:
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
def __init__(self, **kwargs):
self.sometemporaryvariable = "blabla"
super().__init__(**kwargs)

Converting value to json inside serve method. Wagtail

I realy tried to find answer to my question, but don't know what should I do. I found following question and they didn't help me. question1, question2, docs
I got different values with different functions that I used. Sometimes None value
sometimes TypeError: Object of type 'method' is not JSON serializable
and
AttributeError: 'str' object has no attribute 'status_code' and this
TypeError: 'method' object is not iterable
But i didn't still find solution to solve my problem.
Here is my Page model it has InlinePanel that takes some data from another class:
class ScreencastPage(Page):
content_panels = Page.content_panels + [
InlinePanel(
'groupstage_screencast_relationship', label="Choose Teams",
panels=None, max_num=2),
]
parent_page_types = ['home.HomePage']
def matches(self):
matches = [
n.match for n in self.groupstage_screencast_relationship.all()
]
return matches
def serve(self, request):
if request.is_ajax():
# TODO Convert self.mathes to JSON and return it
else:
return super(ScreencastPage, self).serve(request)
And here is the model that related to my ScreencastPage
#register_snippet
class GroupstageTournamentModel(ClusterableModel):
number = models.PositiveSmallIntegerField(
verbose_name="Match №:")
starts_at = models.DateTimeField()
# Team 1
team_1 = models.ForeignKey(
TeamRooster,
null=True, verbose_name='Erste Team',
on_delete=models.SET_NULL,
related_name="+",
)
team_1_dress = ColorField(blank=True, verbose_name='Dress')
team_1_first_halftime_score = models.PositiveSmallIntegerField(blank=True, default=0, verbose_name='Resultat 1. HZ')
team_1_first_halftime_point = models.PositiveSmallIntegerField(blank=True, default=0, verbose_name='Punkte 1. HZ')
...
UPDATE
Sorry if I ask too noob questions, but I'm new in programming. #gasman these are the ways I used.
1
def serve(self, request):
if request.is_ajax():
lst = []
d = {}
for pn in self.matches:
d['mpn']=pn
lst.append(d)
return json.dumps([dict(mpn=pn) for pn in lst])
returns: TypeError: 'method' object is not iterable
2
Just changed loop from for pn in self.matches: to for pn in self.matches():
def serve(self, request):
if request.is_ajax():
lst = []
d = {}
for pn in self.matches():
d['mpn']=pn
lst.append(d)
return json.dumps([dict(mpn=pn) for pn in lst])
returns: TypeError: Object of type 'GroupstageTournamentModel' is not JSON serializable
3
def serve(self, request):
if request.is_ajax():
if isinstance(self.matches, (list, dict, str, int, float, bool, type(None))):
data = JSONEncoder.default(self.matches())
return data
elif '_python_object' in self.matches():
data = pickle.loads(str(self.matches['_python_object']))
return data
returns: ValueError: The view wagtail.wagtailcore.views.serve didn't return an HttpResponse object. It returned None instead.
4
def serve(self, request):
if request.is_ajax():
data = [
n.match for n in self.groupstage_screencast_relationship.all()
]
return data
returns: AttributeError: 'list' object has no attribute 'status_code'
5
def serve(self, request):
if request.is_ajax():
data = [
n.match for n in self.groupstage_screencast_relationship.all()
]
if isinstance(data, (list, dict, str, int, float, bool, type(None))):
conv_data = json.JSONEncoder.default(data)
return conv_data
returns: TypeError: default() missing 1 required positional argument: 'o'
As I said, I do not know how this conversion works, so I tried to guess.
The important lesson here is to try to solve one problem at once. You're trying to deal with returning a response from serve at the same time as constructing some JSON, and it doesn't look like you're getting anywhere because fixing the first half of the problem just leads you to an error in the second half.
Let's make sure we know how to return something from serve, even if it's just something useless:
def serve(self, request):
if request.is_ajax():
return "hello world!"
else:
return super(ScreencastPage, self).serve(request)
This will fail with something like: 'str' object has no attribute 'get'. This tells us that returning a string is the wrong thing to do: whatever object we return, Wagtail is expecting it to have a get attribute. Looking at the documentation, we can see that it's supposed to be an HttpResponse object:
from django.http import HttpResponse
def serve(self, request):
if request.is_ajax():
return HttpResponse("hello world!")
else:
return super(ScreencastPage, self).serve(request)
This works, so now we know that whatever other stuff we do with JSON in this method, we need to end with return HttpResponse(some_result).
So now let's bring in json.dumps. Again, let's start with some fake data to make sure we're using it right:
import json
from django.http import HttpResponse
def serve(self, request):
if request.is_ajax():
result = ['first match', 'second match']
json_output = json.dumps(result)
return HttpResponse(json_output)
else:
return super(ScreencastPage, self).serve(request)
Hopefully this works too, so let's bring in the real data:
import json
from django.http import HttpResponse
def serve(self, request):
if request.is_ajax():
result = self.matches()
json_output = json.dumps(result)
return HttpResponse(json_output)
else:
return super(ScreencastPage, self).serve(request)
This now fails with something like: TypeError: Object of type 'GroupstageTournamentModel' is not JSON serializable. So now you have to ask: what's changed here? What's different about my real data from the 'fake' data? If you're not sure, add in a debugging line to see what's going on:
import json
from django.http import HttpResponse
def serve(self, request):
if request.is_ajax():
result = self.matches()
print(result) # this output will appear in the terminal / command prompt
json_output = json.dumps(result)
return HttpResponse(json_output)
else:
return super(ScreencastPage, self).serve(request)
The error message hopefully makes it clear: the value you're passing to json.dumps contains GroupstageTournamentModel objects, and JSON doesn't know how to deal with those. You need to convert them into basic values such as dicts, and that means specifying how each individual field is meant to appear in the output:
def serve(self, request):
if request.is_ajax():
result = [
{
'number': match.number,
'team1': match.team_1.name,
# ...
}
for match in self.matches()
]
json_output = json.dumps(result)
return HttpResponse(json_output)
else:
return super(ScreencastPage, self).serve(request)
In summary - when you encounter an error message:
don't just abandon your code and try something else;
look at what the error message is telling you, and especially, what line of code it's coming from;
see if there's a way to reduce it to a simpler case that does succeed, then work your way back up to the real solution.

How to solve some sort of chicken egg relation within ndb.Models?

I have two entities (events and users). Each user has several events, but I don't want them to be stored within a StructuredProperty, because in future it should be possible to have multiple creators/admins. Now I have the problem, that User needs the Event Class for definition and vice versa. How can I implement the intended structure?
Two models with mutual relations.
class Event(EndpointsModel):
_message_fields_schema = ("id", "name", "creator",
"datetime", "place", "category", "participants")
creator = ndb.KeyProperty(kind=User)
participants = ndb.KeyProperty(kind=User, repeated=True)
name = ndb.StringProperty()
datetime = ndb.DateTimeProperty(auto_now_add=True)
place = ndb.GeoPtProperty()
category = ndb.StringProperty(choices=('all', 'drinking'))
class User(EndpointsModel):
_message_fields_schema = ("id", "name", "password", "events")
name = ndb.StringProperty()
password = ndb.StringProperty()
events = ndb.KeyProperty(kind=Event, repeated=True)
def create_event(self, e_name, e_datetime, e_place, e_category):
event = Event(name=e_name, creator = self.key, datetime=e_datetime, place=e_place, category=e_category)
event.put()
self.events.append(event)
self.put()
def get_events(self):
return ndb.get_multi(self.events)
Error Message:
NameError: name 'User' is not defined
EDIT 1:
I changed the kind to a string, containing the class name, like Greg suggested it. But it does not work too.
class Category(EndpointsModel):
_message_fields_schema = ("id", "name", "parent")
name = ndb.StringProperty()
parent = ndb.KeyProperty(kind='Category', default=None)
class Event(EndpointsModel):
_message_fields_schema = ("id", "name", "creator", "datetime",
"place", "category")
participants = ndb.KeyProperty(kind='User', repeated=True)
creator = ndb.KeyProperty(kind='User')
name = ndb.StringProperty()
datetime = ndb.DateTimeProperty(auto_now_add=True)
place = ndb.GeoPtProperty()
category = ndb.KeyProperty(Category)
class User(EndpointsModel):
_message_fields_schema = ("id", "name", "password")
name = ndb.StringProperty()
password = ndb.StringProperty()
events = ndb.KeyProperty(Event, repeated=True)
Now I receive the following stack trace:
ERROR 2014-01-21 09:38:39,764 service.py:191] Encountered unexpected error from ProtoRPC method implementation: BadValueError (Expected Key, got [])
Traceback (most recent call last):
File "/home/chris/Downloads/google_appengine/lib/protorpc-1.0/protorpc/wsgi/service.py", line 181, in protorpc_service_app
response = method(instance, request)
File "/home/chris/Downloads/google_appengine/lib/endpoints-1.0/endpoints/api_config.py", line 1321, in invoke_remote
return remote_method(service_instance, request)
[...]
value = self._call_shallow_validation(value)
File "/home/chris/Downloads/google_appengine/google/appengine/ext/ndb/model.py", line 1227, in _call_shallow_validation
return call(value)
File "/home/chris/Downloads/google_appengine/google/appengine/ext/ndb/model.py", line 1274, in call
newvalue = method(self, value)
File "/home/chris/Downloads/google_appengine/google/appengine/ext/ndb/model.py", line 1927, in _validate
raise datastore_errors.BadValueError('Expected Key, got %r' % (value,))
BadValueError: Expected Key, got []
You can use strings in the KeyProperty constructor to refer to kinds that don't have a model definition:
class Event(ndb.Model):
participants = ndb.KeyProperty(kind='User', repeated=True)
You can not create such references to the entities. Here is somo solutions:
1. You must use normal StringProperty for Event.creator or other id for User instance
2. Remove evens from class User - you can reach to evens by index on class Events
3. Use third entity model like this:
class EventCreator(EndpointsModel):
creator = ndb.KeyProperty(kind=User)
event = ndb.KeyProperty(kind=Event)
and from class User remove creator & from class Event remove
You could specify the key properties without the kind parameter (it is optional) and then do a manual check in your constructor or a pre-put hook or something like that -- or maybe not even worry about the kind:
class Event(EndpointsModel):
creator = ndb.KeyProperty()
# Constructor option
def __init__(self, *args, **kwargs):
super(Event, self).__init__(*args, **kwargs)
if 'creator' in kwargs and kwargs['creator'] != 'User':
raise Exception('oh no')
# Hook option
_pre_put_hook(self):
if self.creator and self.creator.kind() != 'User':
raise Exception("oh no")
The actual syntax will probably be slightly different. Feel free to edit.

Slick MSSQL inserting object with auto increment

I've recently had to move a project over from MySQL to MSSQL. I'm using IDENTITY(1,1) on my id columns for my tables to match MySQL's auto-increment feature.
When I try to insert an object though, I'm getting this error:
[SQLServerException: Cannot insert explicit value for identity column in table 'categories' when IDENTITY_INSERT is set to OFF.]
Now after some research I found out that it's because I'm trying to insert a value for my id(0) on my tables. So for example I have an object Category
case class Category(
id: Long = 0L,
name: String
)
object Category extends Table[Category]("categories"){
def name = column[String]("name", O.NotNull)
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def * = id ~ name <> (Category.apply _, Category.unapply _)
def add(model:Category) = withSession{ implicit session =>
Category.insert(model)
}
def remove(id:Long) = withSession{implicit session =>
try{Some(Query(Category).filter(_.id === id).delete)}
catch{case _ => None}
}
}
Is there a way to insert my object into the database and ignoring the 0L without MSSQL throwing an SQLException? MySQL would just ignore the id's value and do the increment like it didn't receive an id.
I'd really rather not create a new case class with everything but the id.
Try redefining your add method like this and see if it works for you:
def add(model:Category) = withSession{ implicit session =>
Category.name.insert(model.name)
}
If you had more columns then you could have added a forInsert projection to your Category table class that specified all fields except id, but since you don't, this should work instead.
EDIT
Now if you do have more than 2 fields on your table objects, then you can do something like this, which is described in the Lifted Embedding documentation here:
case class Category(
id: Long = 0L,
name: String,
foo:String
)
object Category extends Table[Category]("categories"){
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name", O.NotNull)
def foo = column[String]("foo", O.NotNull)
def * = id ~ name ~ foo <> (Category.apply _, Category.unapply _)
def forInsert = name ~ foo <> (t => Category(0L, t._1, t._2), {(c:Category) => Some(c.name, c.foo)})
def add(model:Category) = withSession{ implicit session =>
Category.forInsert insert model
}
def remove(id:Long) = withSession{implicit session =>
try{Some(Query(Category).filter(_.id === id).delete)}
catch{case _ => None}
}
def withSession(f: Session => Unit){
}
}

overwrite existing entity via bulkloader.Loader

I was going to CSV based export/import for large data with app engine. My idea was just simple.
First column of CSV would be key of entity.
If it's not empty, that row means existing entity and should overwrite old one.
Else, that row is new entity and should create new one.
I could export key of entity by adding key property.
class FrontExporter(bulkloader.Exporter):
def __init__(self):
bulkloader.Exporter.__init__(self, 'Front', [
('__key__', str, None),
('name', str, None),
])
But when I was trying to upload CSV, it had failed because bulkloader.Loader.generate_key() was just for "key_name" not "key" itself. That means all exported entities in CSV should have unique 'key_name' if I want to modify-and-reupload them.
class FrontLoader(bulkloader.Loader):
def __init__(self):
bulkloader.Loader.__init__(self, 'Front', [
('_UNUSED', lambda x: None),
('name', lambda x: x.decode('utf-8')),
])
def generate_key(self,i,values):
# first column is key
keystr = values[0]
if len(keystr)==0:
return None
return keystr
I also tried to load key directly without using generate_key(), but both failed.
class FrontLoader(bulkloader.Loader):
def __init__(self):
bulkloader.Loader.__init__(self, 'Front', [
('Key', db.Key), # not working. just create new one.
('__key__', db.Key), # same...
So, how can I overwrite existing entity which has no 'key_name'? It would be horrible if I should give unique name to all entities.....
From the first answer, I could handle this problem. :)
def create_entity(self, values, key_name=None, parent=None):
# if key_name is None:
# print 'key_name is None'
# else:
# print 'key_name=<',key_name,'> : length=',len(key_name)
Validate(values, (list, tuple))
assert len(values) == len(self._Loader__properties), (
'Expected %d columns, found %d.' %
(len(self._Loader__properties), len(values)))
model_class = GetImplementationClass(self.kind)
properties = {
'key_name': key_name,
'parent': parent,
}
for (name, converter), val in zip(self._Loader__properties, values):
if converter is bool and val.lower() in ('0', 'false', 'no'):
val = False
properties[name] = converter(val)
if key_name is None:
entity = model_class(**properties)
#print 'create new one'
else:
entity = model_class.get(key_name)
for key, value in properties.items():
setattr(entity, key, value)
#print 'overwrite old one'
entities = self.handle_entity(entity)
if entities:
if not isinstance(entities, (list, tuple)):
entities = [entities]
for entity in entities:
if not isinstance(entity, db.Model):
raise TypeError('Expected a db.Model, received %s (a %s).' %
(entity, entity.__class__))
return entities
def generate_key(self,i,values):
# first column is key
if values[0] is None or values[0] in ('',' ','-','.'):
return None
return values[0]
Your best option is probably to override create_entity. You'll need to copy most of the existing code there, but modify the constructor to supply a key argument instead of a key_name argument.

Resources