是否可以将两个字段设置为 ndb 中实体的索引? [英] Is it possible to set two fields as indexes on an entity in ndb?

查看:22
本文介绍了是否可以将两个字段设置为 ndb 中实体的索引?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 ndb 和 gae 的新手,在想出一个很好的解决方案设置索引时遇到了问题.假设我们有一个这样的用户模型:

I am new to ndb and gae and have a problem coming up with a good solution setting indexes. Let say we have a user model like this:

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

登录后,如果我要通过查询检查电子邮件地址,我相信这会非常缓慢且效率低下.可能它必须进行全表扫描.

Upon login if I was going to check against the email address with a query, I believe this would be quite slow and inefficient. Possibly it has to do a full table scan.

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

我相信它会更快,如果用户模型以电子邮件作为密钥保存.

I believe it would be much faster, if User models were saved with the email as their key.

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

这样我就可以更快地检索它们(所以我相信)

That way I could retrieve them like this a lot faster (so I believe)

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

到目前为止,如果我错了,请纠正我.但是在实施此操作后,我意识到 Facebook 用户可能会更改他们的电子邮件地址,这样在新的 oauth2.0 连接时,系统无法识别他们的新电子邮件,他们将被创建为新用户.因此,也许我应该使用不同的方法:

So far if I am wrong please correct me. But after implementing this I realized there is a chance that facebook users would change their email address, that way upon a new oauth2.0 connection their new email can't be recognized in the system and they will be created as a new user. Hence maybe I should use a different approach:

  • 使用 social-media-provider-id(所有提供者用户唯一)

  • provider-name(在极少数情况下,两个 twitter 和 facebook 用户共享相同的提供者 ID)

但是为了实现这一点,我需要设置两个索引,我认为这不是 可能.

However in order to achieve this, I needed to set two indexes, which I believe is not possible.

那我能怎么办?我应该将两个字段连接为一个键和索引吗?

So what could I do? Shall I concatenate both fields as a single key and index on that?

例如新的想法是:

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()

这样我们就不再关心用户是否更改了其社交媒体上的电子邮件地址.这个想法合理吗?

This way we don't care any more if the user changes the email address on his social media. Is this idea sound?

谢谢,

更新

Tim 的解决方案迄今为止对我来说听起来最干净,也可能是最快的.但是我遇到了一个问题.

Tim's solution sounded so far the cleanest and likely also the fastest to me. But I came across a problem.

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

推荐答案

您的方法的一个细微变化可能会允许更灵活的模型是为 provider_id、provider_type 创建一个单独的实体,作为密钥或任何其他身份验证你想出的方案

One slight variation on your approach which could allow a more flexible model will be to create a separate entity for the provider_id, provider_type, as the key or any other auth scheme you come up with

此实体然后持有实际用户详细信息的引用(键).

This entity then holds a reference (key) of the actual user details.

然后你可以

  1. 对身份验证详细信息直接执行 get(),然后对实际用户详细信息执行 get().
  2. 无需实际重写/重新键入用户详细信息即可更改身份验证详细信息
  3. 您可以为单个用户支持多个身份验证方案.

我将这种方法用于拥有 > 2000 个用户的应用程序,大多数使用自定义身份验证方案(应用程序特定的用户 ID/密码)或谷歌帐户.

I use this approach for an application that has > 2000 users, most use a custom auth scheme (app specific userid/passwd) or google account.

例如

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() 操作.

By using PolyModel as the base class you can do a AuthLogin.query().filter(AuthLogin.user_key == user.key) and get all auth types defined for that user as they all share the same base class AuthLogin. You need this otherwise you would have to query in turn for each supported auth type, as you can not do a kindless query without an ancestor, and in this case we can't use the User as the ancestor becuase then we couldn't do a simple get() to from the login id.

但是需要注意的是,AuthLogin 的所有子类都将在AuthLogin"键中共享同一种类型,因此您仍然需要将 auth_provider 和 auth_type 连接起来作为键 id,以确保您拥有唯一的键.例如.

However some things to note, all subclasses of AuthLogin will share the same kind in the key "AuthLogin" so you still need to concatenate the auth_provider and auth_type for the keys id so that you can ensure you have unique keys. E.g.

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'])

这就是你遇到的问题.通过将提供程序类型添加为密钥的一部分,您现在可以获得不同的密钥.

This is the problem you ran into. By adding the provider type as part of the key you now get distinct keys.

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> 

我不认为这对您来说是一种不那么方便的模型.

I don't believe this is any less convenient a model for you.

这一切都有意义吗;-)

Does all that make sense ;-)

这篇关于是否可以将两个字段设置为 ndb 中实体的索引?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆