2011-01-07 19 views
2

Im Grunde versucht ich eine Datenstruktur zu erstellen, in der der Benutzername, die ID und das Datum des Benutzers enthalten sind. Dann möchte ich eine "Unterstruktur", wo es den Benutzer "Text" und das Datum hat, das es geändert wurde. und der Benutzer wird mehrere Instanzen dieses Textes haben.Google App Engine Python-Datenspeicher

class User(db.Model): 
    ID = db.IntegerProperty()   
    name = db.StringProperty() 
    datejoined = db.DateTimeProperty(auto_now_add=True) 

class Content(db.Model): 
    text = db.StringProperty() 
    datemod= db.DateTimeProperty(auto_now_add = True) 

Ist der Code korrekt eingerichtet?

Antwort

4

Ein Problem, das Sie haben, ist, dass User.ID einzigartig wird nicht trivial sein. Das Problem ist, dass zwei Schreibvorgänge in der Datenbank auf verschiedenen Shards auftreten können, beide ungefähr gleichzeitig nach vorhandenen Einträgen suchen, die der Eindeutigkeitsbedingung entsprechen und keine finden, dann beide identische Einträge erstellen (in Bezug auf die eindeutige Eigenschaft) und dann Sie einen ungültigen Datenbankstatus haben. Um dieses Problem zu lösen, bietet appengine die Möglichkeit, sicherzustellen, dass bestimmte Datenspeicher-Entitäten immer auf derselben physischen Maschine platziert sind.

Dazu verwenden Sie die Entitätstasten, um Google mitzuteilen, wie die Entitäten zu organisieren sind. Nehmen wir an, Sie möchten, dass der Benutzername eindeutig ist. Ändern User zu wie folgt aussehen:

class User(db.Model): 
    datejoined = db.DateTimeProperty(auto_now_add=True) 

Ja, das ist es wirklich. Es gibt keinen Benutzernamen, da dieser im Schlüssel verwendet wird, sodass er nicht separat angezeigt werden muss. Wenn Sie möchten, können Sie dies tun ...

class User(db.Model): 
    datejoined = db.DateTimeProperty(auto_now_add=True) 

    @property 
    def name(self): 
     return self.key().name() 

Um eine Instanz eines User zu erstellen, müssen Sie jetzt etwas anderes zu tun, müssen Sie eine key_name in der init-Methode angeben.

someuser = User(key_name='john_doe') 
... 
someuser.save() 

Nun, wirklich wollen Sie sicherstellen, dass die Nutzer überschreiben einander nicht, so müssen Sie die Erstellung eines Benutzers in einer Transaktion wickeln. Zuerst eine Funktion definieren, die neccesary Prüfung tut:

def create_user(username): 
    checkeduser = User.get_by_key_name(username) 
    if checkeduser is not None: 
     raise db.Rollback, 'User already exists!' 
    newuser = User(key_name=username) 
    # more code 
    newuser.put() 

Dann rufen Sie es auf diese Weise

db.run_in_transaction(create_user, 'john_doe') 

einen Benutzer zu finden, haben Sie gerade diese:

someuser = User.get_by_key_name('john_doe') 

Als nächstes Sie benötigen eine Möglichkeit, den Inhalt dem Benutzer zuzuordnen, und umgekehrt. Eine Lösung besteht darin, den Inhalt in dieselbe Entitätsgruppe wie den Benutzer zu stellen, indem der Benutzer als übergeordnetes Element des Inhalts deklariert wird. Um dies zu tun, die Sie nicht brauchen, um den Inhalt zu ändern überhaupt, aber Sie es ein wenig anders erstellen (ähnlich wie Sie mit User haben):

somecontent = Content(parent=User.get_by_key_name('john_doe')) 

So gegeben, ein Content-Objekt, können Sie nachschlagen der Benutzer durch seinen Schlüssel Prüfung:

someuser = User.get(somecontent.key().parent()) 

in umgekehrter Richtung gehen, sucht alle Inhalte für einen bestimmten Benutzer auf nur ein wenig komplizierter.

allcontent = Content.gql('where ancestor is :user', user=someuser).fetch(10) 
+0

Dies ist genau das, was ich suche! –

+0

@TokenMacGuy: Mit 'newuser.save()' meinst du 'newuser.put()'? – Kit

1

Ja, und wenn Sie weitere Dokumentation benötigen, können Sie here für Datenbanktypen und here für weitere Informationen über Ihre Modellklassen überprüfen.

1

Eine alternative Lösung, die Sie sehen können, ist die Verwendung von referenceproperty.

class User(db.Model): 
     name = db.StringProperty() 
     datejoined = db.DateTimeProperty(auto_now_add=True) 

    class Content(db.Model): 
     user = db.ReferenceProperty(User,collection_name='matched_content') 
     text = db.StringProperty() 
     datemod= db.DateTimeProperty(auto_now_add = True) 

    content = db.get(content_key) 
    user_name = content.user.name 

    #looking up all of the content for a particular user 
    user_content = content.user.matched_content 

    #create new content for a user 
    new_content = Content(reference=content.user) 
+1

gute Lösung für die Zuordnung der Datentypen, aber schützt Sie nicht vor der Race-Bedingung, die mögliche doppelte Benutzernamen ermöglicht. – SingleNegationElimination

+0

Vielen Dank für den Kommentar –