【问题标题】:"Inner join" like with MongoDB in Flask, Jinja“内部连接”就像在 Flask、Jinja 中使用 MongoDB
【发布时间】:2020-08-27 04:30:31
【问题描述】:

我正在尝试使用 Flask 对两个 MongoDB 集合执行“内部连接”类似

我认为我应该使用$lookup 来实现这一点,而我目前的路线是:

@app.route("/perfumes")
def perfumes():
    perfumes = mongo.db.perfumes.find()
    users = mongo.db.perfumes.aggregate(
        [
            {
                "$lookup": {
                    "from": "mongo.db.users",
                    "localField": "author",
                    "foreignField": "username",
                    "as": "creator",
            }
        }
    ]
)
return render_template("perfumes.html", title="Perfumes", perfumes=perfumes, users=users)

在我的模板中,我正在遍历一个集合中的perfumes,并希望访问users 集合中的所有数据,因为每个香水项目都是由一个用户创建的。 perfumes 集合有一个 author 字段,应该用于链接 users 集合中的 username 字段。 我的模板的一个非常简化(精简)的版本如下所示:

% for perfume in perfumes %}
    {{ perfume.name }}
    Created by {{ <HERE I WANT TO HAVE MY ITEM CREATOR> }} on {{ perfume.date_updated.strftime('%d-%m-%Y') }}
{% endfor %}

为了让这个“join”正常工作并能够访问两个集合中该项目的所有数据,我缺少什么?

谢谢!!

注意:我想访问的不仅仅是创建者的用户名,比如个人资料图片等,这就是我需要加入的原因。

更新:这些是我用于两个数据库的结构:

{
    "perfumes": {
        "author": "<string>",
        "brand": "<string>",
        "name": "<string>",
        "descritpion": "<text field>",
        "date_updated": "<date>",
        "public": "<boolean>",
        "picture": "<string>"
    },
    "users": {
        "username": "<string>",
        "first_name": "<string>",
        "last_name": "<string>",
        "email": "<string>",
        "is_admin": "<boolean>",
        "avatar": "<string>"
    }
}

所以perfumes 中的author 可以与users 中的username 链接。

【问题讨论】:

  • 您能否发布您的收藏外观、少量文档和您的预期输出?
  • 我刚刚更新了我的问题。所以基本上我想访问我模板中的每个项目,并访问其作者的所有数据,这些数据链接到users 中的usernameUsername 是独一无二的。

标签: python mongodb flask flask-pymongo


【解决方案1】:

您可以通过简单的聚合来做到这一点。试试下面的代码:

cur = mongo.db.perfumes.aggregate(
    [
        {
            '$lookup': {
                'from': 'users', # You can directly specify on which collection you want to lookup
                'localField': 'author',
                'foreignField': 'username',
                'as': 'creator',
            }
        },
        {
            '$unwind' : '$creator'
        },
        {
            '$project': {
                'username' : '$creator.username',
                'perfumeName' : '$name',
                'date_updated' : '$date_updated',
                'profilePicture' : '$creator.avatar',
                ..., # Rest of the items that you want to display
                '_id': 0
            }
        }
    ]
)

返回如下结果:

return render_template("perfumes.html", title="Perfumes", perfumes=cur) # You don't need to pass users

在模板中显示如下:

{% for perfume in perfumes %}
    {{ perfume['perfumeName'] }}
    Created by {{ perfume['username'] }} on {{ perfume['date_updated'].strftime('%d-%m-%Y') }}
{% endfor %}

希望对你有帮助。

【讨论】:

  • 这太棒了!它工作得很好!正是我需要的。我很感激!
  • 还有一个问题:假设我有第三个香水系列(类型)。我怎样才能在香水.type 字段中“加入”那个,这样我就可以在模板中找到所有 3 个?
  • 我怎样才能传递(到另一个模板)该游标的一条记录?在聚合中找到一个的方法是什么?我希望再次显示单个记录的所有交叉信息(不涉及迭代)
  • 要加入第三个集合,您应该再次使用$lookup 运算符。您可以通过此链接了解它:stackoverflow.com/questions/35813854/…。如果您需要任何进一步的帮助,您可以发布一个单独的问题并让我知道。
  • 要在聚合中找到一个,您应该在您的唯一字段/主键上使用 $match 运算符。但这也会产生一个文档列表,但长度为一个。
【解决方案2】:

您可以创建一个 jinja 过滤器来连接来自两个光标的信息,然后在 jinja 上对其进行迭代,就像它是一个 dicts 列表一样。

开始创建自定义 jinja 过滤器,其中聚合光标是您的 mongo.db.perfumes.aggregate 行的输出:例如page_filters.py 文件

from .. import app

@app.template_filter()
def get_user_information(perfume_cursor):
    """ Creates a generator to get extra info of user """
    # Transform mongo cursor into a list of dicts
    perfume_cursor = list(perfume_cursor)

    for item in perfume_cursor:
        extra_info = mongo.db.users.find_one({"username": item['author']}, {"first_name": 1, "profile_picture": 1})
        # Declare below your desired user info
        item['user_info'] = dict(first_name=extra_info.first_name,profile_picture=extra_info.profile_picture)
        yield item

要使我们的过滤器在模板中可用,我们只需将其导入顶级 init.py

# Make sure app has been initialized first to prevent circular imports.
from .util import page_filters

现在在 jinja 上,只需使用过滤器

{% for perfume in (perfumes | get_user_information) %}
    {{ perfume['name'] }}
    Created by {{ perfume['user_info']['first_name']}} on {{ perfume['date_updated'].strftime('%d-%m-%Y') }}
{% endfor %}

您应该使查询更加具体,只获取必要的用户字段,否则您的记忆中可能会有太多信息。我们使用 Generator 的原因是提供一种处理数据的方法,而无需先将整个数据源加载到内存中。

参考: https://uniwebsidad.com/libros/explore-flask/chapter-8/custom-filters https://www.lachlaneagling.com/reducing-memory-consumption-python/

【讨论】:

  • 接下来我正在尝试这个,所以,非常感谢!惊叹于这里的善良和专业知识!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2016-10-01
  • 2021-04-29
  • 1970-01-01
  • 2013-03-14
  • 2020-08-31
相关资源
最近更新 更多