Jednym ze sposobów, aby zrobić to szybciej, jest przekształcenie modelu w słownik i użycie natywnej funkcji eval/repr jako swoich (de) serializerów - z ostrożnością oczywiście, jak zawsze w przypadku złego ewaluacji, ale powinno to być bądź bezpieczny tutaj, ponieważ nie ma zewnętrznego kroku.
Poniżej przykład klasy Fake_entity implementującej dokładnie to. Najpierw utwórz słownik przez fake = Fake_entity(entity)
, a następnie po prostu zapisz dane za pomocą memcache.set(key, fake.serialize())
. Funkcja serialize() jest prostym wywołaniem metody repr z natywnego słownika, z pewnymi dodatkami, jeśli potrzebujesz (np. Dodaj identyfikator na początku łańcucha).
Aby pobrać, użyj po prostu fake = Fake_entity(memcache.get(key))
. Obiekt Fake_entity jest prostym słownikiem, którego klucze są również dostępne jako atrybuty. Możesz uzyskać dostęp do właściwości obiektu w normalny sposób, z tym że referenceProperties daje klucze zamiast pobierania obiektu (co jest całkiem przydatne). Możesz również uzyskać() rzeczywistą jednostkę za pomocą fake.get() lub bardziej interesująco, zmień ją, a następnie zapisz za pomocą fake.put().
Nie działa z listami (jeśli pobierasz wiele obiektów z zapytania), ale można je łatwo dostosować za pomocą funkcji łączenia/podziału, używając identyfikatora takiego jak "### FAKE MODEL ENTITY ###" jako separatora . Używaj tylko z db.Model, wymagałoby drobnych korekt dla Expando.
class Fake_entity(dict):
def __init__(self, record):
# simple case: a string, we eval it to rebuild our fake entity
if isinstance(record, basestring):
import datetime # <----- put all relevant eval imports here
from google.appengine.api import datastore_types
self.update(eval(record)) # careful with external sources, eval is evil
return None
# serious case: we build the instance from the actual entity
for prop_name, prop_ref in record.__class__.properties().items():
self[prop_name] = prop_ref.get_value_for_datastore(record) # to avoid fetching entities
self['_cls'] = record.__class__.__module__ + '.' + record.__class__.__name__
try:
self['key'] = str(record.key())
except Exception: # the key may not exist if the entity has not been stored
pass
def __getattr__(self, k):
return self[k]
def __setattr__(self, k, v):
self[k] = v
def key(self):
from google.appengine.ext import db
return db.Key(self['key'])
def get(self):
from google.appengine.ext import db
return db.get(self['key'])
def put(self):
_cls = self.pop('_cls') # gets and removes the class name form the passed arguments
# import xxxxxxx ---> put your model imports here if necessary
Cls = eval(_cls) # make sure that your models declarations are in the scope here
real_entity = Cls(**self) # creates the entity
real_entity.put() # self explanatory
self['_cls'] = _cls # puts back the class name afterwards
return real_entity
def serialize(self):
return '### FAKE MODEL ENTITY ###\n' + repr(self)
# or simply repr, but I use the initial identifier to test and eval directly when getting from memcache
Chciałbym powitać testy prędkości na tym, zakładam, że jest to znacznie szybciej niż inne podejścia. Dodatkowo, nie masz żadnego ryzyka, jeśli twoje modele w międzyczasie uległy zmianie.
Poniżej przykład tego, jak wygląda serializowana podróbka.Weźmy konkretny przyjrzeć datetime (utworzone), jak również właściwości referencyjnych (subdomeny):
### FAKE MODEL PODMIOT ###
{ 'status': u'admin', 'session_expiry': None " first_name ': u'Louis', 'last_name': u'Le Sieur ',' modified_by ': None,' password_hash ': u'a9993e364706816aba3e25717000000000000000', 'language': u'fr ',' created ': datetime.datetime (2010, 7, 18, 21, 50, 11, 750000), "zmodyfikowane": Brak, "created_by": Brak, "email": u'
[email protected] ',' key ':' agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw ',' session_ref ": Brak," _cls ":" models.Login "," grupy ": []," email___password_hash ": u'
[email protected]+a9993e364706816aba3e25717000000000000000 ',' subdomena ': datastore_types.Key.from_path (u'Subdomain' , 229L, _app = u'jeregle ")," dozwolone ": []," uprawnienia ": []}
Osobiście używam również zmiennych statycznych (szybszych niż memcache) do buforowania moich obiektów w krótkim czasie i pobierania magazynu danych, gdy serwer się zmienił lub jego pamięć została wyczyszczona z jakiegoś powodu (co zdarza się dość często w rzeczywistości) .
Po prostu wypróbowałem to z naprawdę dużymi i złożonymi modelami, ale wynik był prawie taki sam. –
Może jest http://docs.python.org/library/timeit.html na GAE? Powinno to pokazywać dokładniejsze wyniki, ale mimo to - po przeczytaniu wpisu na blogu, do którego się przyłączyłeś, oczekiwałbym różnicy wielkości między wydajnością protobufferów a piklami - i to powinno być przechwycone przez time.time() mimo wszystko .. –
używając java appengine, więc jestem zbyt leniwy, aby przetestować tę teorię - jest to pikle() buforujące wyniki za kulisami, podczas gdy to_protobuf nie jest? Opierając się na artykule, nie jestem pewien, czy spodziewałbym się wzrostu prędkości o cały rząd wielkości, ponieważ pikle wciąż są wywoływane nawet przy użyciu wersji protobuf. Wykorzystana przestrzeń z pewnością mogłaby być znacznie mniejsza. –