2

私はndbとgaeが初めてで、インデックスを設定する良い解決策を考え出すのに問題があります。次のようなユーザー モデルがあるとします。

class User(ndb.Model):
    name = ndb.StringProperty()    
    email = ndb.StringProperty(required = True)    
    fb_id = ndb.StringProperty()

ログイン時にクエリで電子メール アドレスを確認しようとすると、これは非常に遅く、非効率的であると思います。おそらく、完全なテーブル スキャンを実行する必要があります。

q = User.query(User.email == EMAIL)
user = q.fetch(1)

ユーザーモデルがメールをキーとして保存されていれば、はるかに高速になると思います。

user = user(id=EMAIL)
user.put()

そうすれば、このようにそれらをより速く取得できます(そう信じています)

key = ndb.Key('User', EMAIL) 
user = key.get()

これまでのところ、私が間違っている場合は修正してください。しかし、これを実装した後、Facebook ユーザーがメールアドレスを変更する可能性があることに気付きました。新しい oauth2.0 接続時に、新しいメールがシステムで認識されず、新しいユーザーとして作成されます。したがって、おそらく別のアプローチを使用する必要があります。

  • social-media-provider-id の使用 (すべてのプロバイダー ユーザーに固有)

  • provider-name (まれに、2 人の Twitter ユーザーと Facebook ユーザーが同じプロバイダー ID を共有する場合があります)

ただし、これを実現するには、2 つのインデックスを設定する必要がありましたが、これは不可能だと思います。

それで、私は何ができるでしょうか?両方のフィールドを単一のキーとインデックスとして連結しますか?

たとえば、新しいアイデアは次のようになります。

class User(ndb.Model):
    name = ndb.StringProperty()    
    email = ndb.StringProperty(required = True)    
    provider_id = ndb.StringProperty()
    provider_type = ndb.StringProperty()

保存:

provider_id = 1234
provider_type = fb
user = user(id=provider_id + provider_type)
user.put()

検索:

provider_id = 1234
provider_type = fb
key = ndb.Key('User', provider_id + provider_type) 
user = key.get()

これにより、ユーザーがソーシャル メディアでメール アドレスを変更しても、気にする必要がなくなります。この考えは正しいですか?

ありがとう、

アップデート

ティムのソリューションは、これまでのところ最もクリーンで、おそらく最速でもありました。しかし、私は問題に遭遇しました。

class AuthProvider(polymodel.PolyModel):
    user_key = ndb.KeyProperty(kind=User)
    active = ndb.BooleanProperty(default=True)  
    date_created = ndb.DateTimeProperty(auto_now_add=True)

    @property
    def user(self):
        return self.user_key.get()

class FacebookLogin(AuthProvider):
    pass

View.py: facebook_callback メソッド内

provider = ndb.Key('FacebookLogin', fb_id).get() 

# Problem is right here. provider is always None. Only if I used the PolyModel like this:
# ndb.Key('AuthProvider', fb_id).get()
#But this defeats the whole purpose of having different sub classes as different providers. 
#Maybe I am using the key handeling wrong?


if provider:
    user = provider.user
else:
    provider = FacebookLogin(id=fb_id)          
if not user:
        user = User()
        user_key = user.put()
        provider.user_key = user_key
        provider.put() 
return user
4

4 に答える 4

3

より柔軟なモデルを可能にするアプローチのわずかなバリエーションの 1 つは、provider_id、provider_type の別のエンティティを、キーまたは思いついた他の認証スキームとして作成することです。

このエンティティは、実際のユーザー詳細の参照 (キー) を保持します。

その後、次のことができます

  1. 認証の詳細に対して直接 get() を実行してから、実際のユーザーの詳細を get() します。
  2. 認証の詳細は、ユーザーの詳細を実際に書き換えたりキーを変更したりせずに変更できます
  3. 1 人のユーザーに対して複数の認証スキームをサポートできます。

私は 2000 人を超えるユーザーを持つアプリケーションにこのアプローチを使用します。ほとんどのユーザーは、カスタム認証スキーム (アプリ固有のユーザー ID/パスワード) または Google アカウントを使用します。

例えば

class AuthLogin(ndb.Polymodel):
     user_key = ndb.KeyProperty(kind=User)
     status = ndb.StringProperty()  # maybe you need to disable a particular login with out deleting it.
     date_created = ndb.DatetimeProperty(auto_now_add=True)

     @property
     def user(self):
         return self.user_key.get()


class FacebookLogin(AuthLogin):
    # some additional facebook properties

class TwitterLogin(AuthLogin):
    # Some additional twitter specific properties

等...

基本クラスとして PolyModel を使用することでAuthLogin.query().filter(AuthLogin.user_key == user.key) 、同じ基本クラス AuthLogin をすべて共有するため、そのユーザーに対して定義されたすべての認証タイプを実行して取得できます。そうしないと、サポートされている認証タイプごとに順番にクエリを実行する必要があります。これは、先祖なしでは種類のないクエリを実行Userできないためです。ログインIDから単純なget()。

ただし、注意すべき点として、AuthLogin のすべてのサブクラスはキー「AuthLogin」で同じ種類を共有するため、キー ID の auth_provider と auth_type を連結して、一意のキーを確保できるようにする必要があります。例えば

dev~fish-and-lily> from google.appengine.ext.ndb.polymodel import PolyModel
dev~fish-and-lily> class X(PolyModel):
...    pass
... 
dev~fish-and-lily> class Y(X):
...    pass
... 
dev~fish-and-lily> class Z(X):
...    pass
... 
dev~fish-and-lily> y = Y(id="abc")
dev~fish-and-lily> y.put()
Key('X', 'abc')
dev~fish-and-lily> z = Z(id="abc")
dev~fish-and-lily> z.put()
Key('X', 'abc')
dev~fish-and-lily> y.key.get()
Z(key=Key('X', 'abc'), class_=[u'X', u'Z'])

dev~fish-and-lily> z.key.get()
Z(key=Key('X', 'abc'), class_=[u'X', u'Z'])

これがあなたが遭遇した問題です。プロバイダーの種類をキーの一部として追加することで、個別のキーを取得できるようになりました。

dev~fish-and-lily> z = Z(id="Zabc")
dev~fish-and-lily> z.put()
Key('X', 'Zabc')
dev~fish-and-lily> y = Y(id="Yabc")
dev~fish-and-lily> y.put()
Key('X', 'Yabc')
dev~fish-and-lily> y.key.get()
Y(key=Key('X', 'Yabc'), class_=[u'X', u'Y'])
dev~fish-and-lily> z.key.get()
Z(key=Key('X', 'Zabc'), class_=[u'X', u'Z'])
dev~fish-and-lily> 

これがあなたにとって不便なモデルだとは思いません。

それはすべて理にかなっていますか;-)

于 2013-07-03T00:58:49.797 に答える
2

@Gregの答えはOKのようですが、このソリューションはうまくスケーリングできないため、エンティティのキ​​ーとして外部タイプ/ IDを関連付けるのは実際には悪い考えだと思います。

  • ある時点で独自のユーザー名/パスワードを実装したい場合はどうしますか?
  • ユーザーが自分の Facebook アカウントを削除しようとするとどうなりますか?
  • 同じユーザーが Twitter アカウントでもサインインしたい場合はどうなりますか?
  • ユーザーが複数の Facebook アカウントを持っている場合はどうなりますか?

そのため、キーとしてタイプ/ID を持つという考えは弱そうに見えます。より良い解決策は、すべてのタイプに ID のみを格納するフィールドを用意することです。たとえば、、facebook_idなどtwitter_idgoogle_idこれらのフィールドに対してクエリを実行して、実際のユーザーを取得します。これは、サインインおよびサインアップ プロセス中に発生するため、それほど頻繁ではありません。もちろん、既存のユーザーに別のプロバイダーを追加するロジックを追加するか、同じユーザーが別のプロバイダーでサインインした場合はユーザーをマージする必要があります。

それでも、同じプロバイダーからの複数のサインインをサポートする場合、最後のソリューションは機能しません。これを実現するには、外部プロバイダー/ID のみを保存し、それらをユーザー モデルに関連付ける別のモデルを作成する必要があります。

2 番目のソリューションの例として、 3 つの異なるプロバイダーをUserモデルに格納し、 auth.pyモジュールでそれらに取り組んでいる私の gae -initプロジェクトを確認できます。繰り返しますが、このソリューションは、より多くのプロバイダーにうまく対応できず、同じプロバイダーからの複数の ID をサポートしていません。

于 2013-07-02T23:34:49.420 に答える