【Python百日进阶-Web开发-Peewee】Day261 - Peewee 表关系

文章目录

  • 十、 关系和连接
    • 10.1 模型定义
      • 10.1.1 创建测试数据
    • 10.2 执行简单连接
    • 10.3 连接多个表
    • 10.4 从多个来源中选择
      • 10.4.1 更复杂的例子
    • 10.5 子查询
      • 10.5.1 公用表表达式
    • 10.6 同一模型的多个外键
    • 10.7 加入任意字段

十、 关系和连接

在本文档中,我们将介绍 Peewee 如何处理模型之间的关系。

10.1 模型定义

我们将在示例中使用以下模型定义:

import datetime
from peewee import *db = SqliteDatabase(':memory:')class BaseModel(Model):class Meta:database = dbclass User(BaseModel):username = TextField()class Tweet(BaseModel):content = TextField()timestamp = DateTimeField(default=datetime.datetime.now)user = ForeignKeyField(User, backref='tweets')class Favorite(BaseModel):user = ForeignKeyField(User, backref='favorites')tweet = ForeignKeyField(Tweet, backref='favorites')

Peewee 用于ForeignKeyField定义模型之间的外键关系。每个外键字段都有一个隐含的反向引用,它使用提供的属性作为预过滤Select查询 公开。backref

10.1.1 创建测试数据

为了跟随示例,让我们用一些测试数据填充这个数据库:

def populate_test_data():db.create_tables([User, Tweet, Favorite])data = (('huey', ('meow', 'hiss', 'purr')),('mickey', ('woof', 'whine')),('zaizee', ()))for username, tweets in data:user = User.create(username=username)for tweet in tweets:Tweet.create(user=user, content=tweet)# Populate a few favorites for our users, such that:favorite_data = (('huey', ['whine']),('mickey', ['purr']),('zaizee', ['meow', 'purr']))for username, favorites in favorite_data:user = User.get(User.username == username)for content in favorites:tweet = Tweet.get(Tweet.content == content)Favorite.create(user=user, tweet=tweet)

这给了我们以下信息:

UserTweetFavorited by
hueymeowzaizee
hueyhiss
hueypurrmickey, zaizee
mickeywoof
mickeywhinehuey

在以下示例中,我们将执行一些查询。如果您不确定正在执行多少查询,您可以添加以下代码,它将所有查询记录到控制台:

import logging
logger = logging.getLogger('peewee')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)

笔记

在 SQLite 中,默认情况下不启用外键。大多数事情,包括 Peewee 外键 API,都可以正常工作,但 ON DELETE
行为将被忽略,即使您on_delete在 ForeignKeyField. 结合默认 AutoField行为(可以重用已删除的记录
ID),这可能会导致细微的错误。为避免出现问题,我建议您在使用 SQLite 时启用外键约束,方法是
在实例化时设置.pragmas={‘foreign_keys’: 1}SqliteDatabase

# Ensure foreign-key constraints are enforced.
db = SqliteDatabase('my_app.db', pragmas={'foreign_keys': 1})

10.2 执行简单连接

作为学习如何使用 Peewee 执行连接的练习,让我们编写一个查询以打印出“huey”的所有推文。为此,我们将从 Tweet模型中选择并加入User模型,然后我们可以在字段上进行过滤 User.username:


>>> query = Tweet.select().join(User).where(User.username == 'huey')
>>> for tweet in query:
...     print(tweet.content)
...
meow
hiss
purr

笔记

我们不必明确指定连接谓词(“ON”子句),因为 Peewee 从模型中推断,当我们从 Tweet
连接到用户时,我们是Tweet.user在外键上连接的。

以下代码是等效的,但更明确:

query = (Tweet.select().join(User, on=(Tweet.user == User.id)).where(User.username == 'huey'))

如果我们已经有了User对“huey”对象的引用,我们可以使用User.tweets反向引用来列出所有huey的推文:

>>> huey = User.get(User.username == 'huey')
>>> for tweet in huey.tweets:
...     print(tweet.content)
...
meow
hiss
purr

仔细看huey.tweets,我们可以看到它只是一个简单的预过滤SELECT查询:

>>> huey.tweets
<peewee.ModelSelect at 0x7f0483931fd0>>>> huey.tweets.sql()
('SELECT "t1"."id", "t1"."content", "t1"."timestamp", "t1"."user_id"FROM "tweet" AS "t1" WHERE ("t1"."user_id" = ?)', [1])

10.3 连接多个表

让我们通过查询用户列表并获取他们创作的推文的数量被收藏来再次查看连接。这将需要我们加入两次:从用户到推文,以及从推文到收藏。我们将添加额外的要求,即应包括尚未创建任何推文的用户,以及推文未被收藏的用户。以 SQL 表示的查询将是:

SELECT user.username, COUNT(favorite.id)
FROM user
LEFT OUTER JOIN tweet ON tweet.user_id = user.id
LEFT OUTER JOIN favorite ON favorite.tweet_id = tweet.id
GROUP BY user.username

笔记

在上面的查询中,两个连接都是 LEFT OUTER,因为用户可能没有任何推文,或者,如果他们有推文,它们可能都没有被收藏。

Peewee 有一个join context的概念,这意味着每当我们调用该 join()方法时,我们都会隐式地加入先前加入的模型(或者如果这是第一次调用,则我们正在从中选择模型)。由于我们是直接加入的,从用户到推文,然后从推文到收藏,我们可以简单地写:

query = (User.select(User.username, fn.COUNT(Favorite.id).alias('count')).join(Tweet, JOIN.LEFT_OUTER)  # Joins user -> tweet..join(Favorite, JOIN.LEFT_OUTER)  # Joins tweet -> favorite..group_by(User.username))

迭代结果:

>>> for user in query:
...     print(user.username, user.count)
...
huey 3
mickey 1
zaizee 0

对于涉及多个连接和切换连接上下文的更复杂的示例,让我们查找 Huey 的所有推文以及它们被收藏的次数。为此,我们需要执行两次连接,并且我们还将使用聚合函数来计算收藏次数。

下面是我们如何在 SQL 中编写此查询:

SELECT tweet.content, COUNT(favorite.id)
FROM tweet
INNER JOIN user ON tweet.user_id = user.id
LEFT OUTER JOIN favorite ON favorite.tweet_id = tweet.id
WHERE user.username = 'huey'
GROUP BY tweet.content;

笔记

我们使用从推文到收藏夹的 LEFT OUTER 连接,因为推文可能没有任何收藏夹,但我们仍然希望在结果集中显示它的内容(以及零计数)。

使用 Peewee,生成的 Python 代码看起来非常类似于我们用 SQL 编写的代码:

query = (Tweet.select(Tweet.content, fn.COUNT(Favorite.id).alias('count')).join(User)  # Join from tweet -> user..switch(Tweet)  # Move "join context" back to tweet..join(Favorite, JOIN.LEFT_OUTER)  # Join from tweet -> favorite..where(User.username == 'huey').group_by(Tweet.content))

请注意对switch() 的调用,它指示 Peewee 将连接上下文设置回Tweet. 如果我们省略了对 switch 的显式调用,Peewee 将使用User(我们加入的最后一个模型)作为连接上下文,并使用 Favorite.user外键构造从用户到收藏夹的连接,这会给我们带来不正确的结果。

如果我们想省略连接上下文切换,我们可以改用该 join_from()方法。以下查询等效于前一个查询:

query = (Tweet.select(Tweet.content, fn.COUNT(Favorite.id).alias('count')).join_from(Tweet, User)  # Join tweet -> user..join_from(Tweet, Favorite, JOIN.LEFT_OUTER)  # Join tweet -> favorite..where(User.username == 'huey').group_by(Tweet.content))

我们可以遍历上述查询的结果以打印推文的内容和收藏次数:

>>> for tweet in query:
...     print('%s favorited %d times' % (tweet.content, tweet.count))
...
meow favorited 1 times
hiss favorited 0 times
purr favorited 2 times

10.4 从多个来源中选择

如果我们希望列出数据库中的所有推文及其作者的用户名,您可以尝试这样写:

>>> for tweet in Tweet.select():
...     print(tweet.user.username, '->', tweet.content)
...
huey -> meow
huey -> hiss
huey -> purr
mickey -> woof
mickey -> whine

上面的循环有一个大问题:它为每条推文执行一个额外的查询来查找tweet.user外键。对于我们的小表,性能损失并不明显,但我们会发现延迟随着行数的增加而增加。

如果您熟悉 SQL,您可能还记得可以从多个表中进行 SELECT,从而允许我们在单个查询中获取推文内容和用户名:

SELECT tweet.content, user.username
FROM tweet
INNER JOIN user ON tweet.user_id = user.id;

Peewee 使这很容易。事实上,我们只需要稍微修改一下我们的查询。我们告诉 Peewee 我们希望选择Tweet.content该User.username字段,然后我们包含从推文到用户的连接。为了更清楚地表明它正在做正确的事情,我们可以要求 Peewee 将行作为字典返回。

>>> for row in Tweet.select(Tweet.content, User.username).join(User).dicts():
...     print(row)
...
{'content': 'meow', 'username': 'huey'}
{'content': 'hiss', 'username': 'huey'}
{'content': 'purr', 'username': 'huey'}
{'content': 'woof', 'username': 'mickey'}
{'content': 'whine', 'username': 'mickey'}

现在我们将停止对“.dicts()”的调用并将行作为Tweet 对象返回。请注意,Peewee 将username值分配给 tweet.user.username- NOT tweet.username!因为从 tweet 到 user 有一个外键,并且我们从两个模型中选择了字段,所以 Peewee 将为我们重建模型图:

>>> for tweet in Tweet.select(Tweet.content, User.username).join(User):
...     print(tweet.user.username, '->', tweet.content)
...
huey -> meow
huey -> hiss
huey -> purr
mickey -> woof
mickey -> whine

如果我们愿意,我们可以通过在方法中User指定 an 来控制 Peewee 在上述查询中放置连接实例的位置:attrjoin()

>>> query = Tweet.select(Tweet.content, User.username).join(User, attr='author')
>>> for tweet in query:
...     print(tweet.author.username, '->', tweet.content)
...
huey -> meow
huey -> hiss
huey -> purr
mickey -> woof
mickey -> whine

相反,如果我们只是希望我们选择的所有属性都是Tweet实例的属性,我们可以objects()在查询末尾添加一个调用(类似于我们调用的方式dicts()):

>>> for tweet in query.objects():
...     print(tweet.username, '->', tweet.content)
...
huey -> meow
(etc)

10.4.1 更复杂的例子

作为一个更复杂的示例,在此查询中,我们将编写一个查询,该查询选择所有收藏夹,以及创建收藏夹的用户、收藏的推文以及该推文的作者。

在 SQL 中,我们会写:

SELECT owner.username, tweet.content, author.username AS author
FROM favorite
INNER JOIN user AS owner ON (favorite.user_id = owner.id)
INNER JOIN tweet ON (favorite.tweet_id = tweet.id)
INNER JOIN user AS author ON (tweet.user_id = author.id);

请注意,我们从用户表中选择了两次——一次是在创建收藏夹的用户的上下文中,另一次是作为推文的作者。

使用 Peewee,我们使用Model.alias()别名模型类,以便可以在单个查询中引用它两次:

Owner = User.alias()
query = (Favorite.select(Favorite, Tweet.content, User.username, Owner.username).join(Owner)  # Join favorite -> user (owner of favorite)..switch(Favorite).join(Tweet)  # Join favorite -> tweet.join(User))   # Join tweet -> user

我们可以通过以下方式遍历结果并访问连接的值。请注意 Peewee 如何从我们选择的各种模型中解析字段并重建模型图:

>>> for fav in query:
...     print(fav.user.username, 'liked', fav.tweet.content, 'by', fav.tweet.user.username)
...
huey liked whine by mickey
mickey liked purr by huey
zaizee liked meow by huey
zaizee liked purr by huey

10.5 子查询

Peewee 允许您加入任何类似表的对象,包括子查询或公用表表达式 (CTE)。为了演示加入子查询,让我们查询所有用户及其最新推文。

这是SQL:

SELECT tweet.*, user.*
FROM tweet
INNER JOIN (SELECT latest.user_id, MAX(latest.timestamp) AS max_tsFROM tweet AS latestGROUP BY latest.user_id) AS latest_query
ON ((tweet.user_id = latest_query.user_id) AND (tweet.timestamp = latest_query.max_ts))
INNER JOIN user ON (tweet.user_id = user.id)

我们将通过创建一个选择每个用户及其最新推文时间戳的子查询来做到这一点。然后我们可以在外部查询中查询推文表,并从子查询中加入用户和时间戳组合。

# Define our subquery first. We'll use an alias of the Tweet model, since
# we will be querying from the Tweet model directly in the outer query.
Latest = Tweet.alias()
latest_query = (Latest.select(Latest.user, fn.MAX(Latest.timestamp).alias('max_ts')).group_by(Latest.user).alias('latest_query'))# Our join predicate will ensure that we match tweets based on their
# timestamp *and* user_id.
predicate = ((Tweet.user == latest_query.c.user_id) &(Tweet.timestamp == latest_query.c.max_ts))# We put it all together, querying from tweet and joining on the subquery
# using the above predicate.
query = (Tweet.select(Tweet, User)  # Select all columns from tweet and user..join(latest_query, on=predicate)  # Join tweet -> subquery..join_from(Tweet, User))  # Join from tweet -> user.

遍历查询,我们可以看到每个用户及其最新的推文。

>>> for tweet in query:
...     print(tweet.user.username, '->', tweet.content)
...
huey -> purr
mickey -> whine

在我们用于在本节中创建查询的代码中,有几件事您可能以前没有见过:

  • 我们曾经join_from()明确指定连接上下文。我们写了,相当于 …join_from(Tweet, User).switch(Tweet).join(User)
  • 我们使用魔法属性引用了子查询中的列.c,例如latest_query.c.max_ts. 该.c属性用于动态创建列引用。
  • 我们没有将单个字段传递给Tweet.select(),而是传递了 TweetandUser模型。这是选择给定模型上所有字段的简写。

10.5.1 公用表表达式

在上一节中,我们加入了子查询,但我们也可以轻松地使用公共表表达式 (CTE)。我们将重复与之前相同的查询,列出用户及其最新的推文,但这次我们将使用 CTE 来完成。

这是SQL:

WITH latest AS (SELECT user_id, MAX(timestamp) AS max_tsFROM tweetGROUP BY user_id)
SELECT tweet.*, user.*
FROM tweet
INNER JOIN latestON ((latest.user_id = tweet.user_id) AND (latest.max_ts = tweet.timestamp))
INNER JOIN userON (tweet.user_id = user.id)

这个例子看起来与前面带有子查询的例子非常相似:

# Define our CTE first. We'll use an alias of the Tweet model, since
# we will be querying from the Tweet model directly in the main query.
Latest = Tweet.alias()
cte = (Latest.select(Latest.user, fn.MAX(Latest.timestamp).alias('max_ts')).group_by(Latest.user).cte('latest'))# Our join predicate will ensure that we match tweets based on their
# timestamp *and* user_id.
predicate = ((Tweet.user == cte.c.user_id) &(Tweet.timestamp == cte.c.max_ts))# We put it all together, querying from tweet and joining on the CTE
# using the above predicate.
query = (Tweet.select(Tweet, User)  # Select all columns from tweet and user..join(cte, on=predicate)  # Join tweet -> CTE..join_from(Tweet, User)  # Join from tweet -> user..with_cte(cte))

我们可以遍历结果集,其中包含每个用户的最新推文:


>>> for tweet in query:
...     print(tweet.user.username, '->', tweet.content)
...
huey -> purr
mickey -> whine

笔记

有关使用 CTE 的更多信息,包括有关编写递归 CTE 的信息,请参阅“查询”文档的公用表表达式部分。

10.6 同一模型的多个外键

当同一模型有多个外键时,最好明确指定要加入的字段。

回顾示例应用程序的模型,考虑关系模型,它用于表示一个用户何时关注另一个用户。这是模型定义:

class Relationship(BaseModel):from_user = ForeignKeyField(User, backref='relationships')to_user = ForeignKeyField(User, backref='related_to')class Meta:indexes = (# Specify a unique multi-column index on from/to-user.(('from_user', 'to_user'), True),)

由于User有两个外键,我们应该始终指定我们在连接中使用哪个字段。

例如,要确定我关注哪些用户,我会写:

(User.select().join(Relationship, on=Relationship.to_user).where(Relationship.from_user == charlie))

另一方面,如果我想确定哪些用户在关注我,我会加入from_user列并过滤关系的 to_user:

(User.select().join(Relationship, on=Relationship.from_user).where(Relationship.to_user == charlie))

10.7 加入任意字段

如果两个表之间不存在外键,您仍然可以执行连接,但您必须手动指定连接谓词。

在以下示例中, User和 ActivityLog之间没有显式外键,但ActivityLog.object_id字段和User.id之间存在隐含关系 。Field我们将使用加入,而不是加入特定 的Expression.

user_log = (User.select(User, ActivityLog).join(ActivityLog, on=(User.id == ActivityLog.object_id), attr='log').where((ActivityLog.activity_type == 'user_activity') &(User.username == 'charlie')))for user in user_log:print(user.username, user.log.description)#### Print something like ####
charlie logged in
charlie posted a tweet
charlie retweeted
charlie posted a tweet
charlie logged out

笔记

attr回想一下,我们可以通过在方法中指定参数来控制 Peewee 将分配连接实例的属性join()。在前面的示例中,我们使用了以下连接:

join(ActivityLog, on=(User.id == ActivityLog.object_id), attr='log')

然后在遍历查询时,我们能够直接访问加入的ActivityLog,而不会产生额外的查询:

for user in user_log:print(user.username, user.log.description)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用Adobe Acrobat DC实现图片和PDF互相转换

一、图片转PDF 可以实现多张或者单张图片转PDF! 这个需要使用到Adobe Acrobat DC的文件合并功能&#xff0c;因为Adobe Acrobat DC的文件合并&#xff0c;不仅可以合并多个pdf文件&#xff0c;也支持合并图片文件。 如图&#xff0c;直接选中多张图片合成即可&#xff1a; 添…

微信小程序上线与发布图文步骤操作

1.上传代码 打开微信小程序&#xff0c;在微信开发者工具的工具栏中单击“上传”按钮&#xff0c;页面中弹出提示框&#xff0c;根据提示填写相应的信息&#xff0c;然后单击“上传”按钮&#xff0c;即可上传代码。 2.查看上传代码之后的版本 登录微信小程序管理后台&…

数仓报表数据导出——Hive数据导出至Clickhouse

1. Clickhouse建表 创建database create database ad_report; use ad_report;创建table drop table if exists dwd_ad_event_inc; create table if not exists dwd_ad_event_inc (event_time Int64 comment 事件时间,event_type String comment 事件…

python爬虫_django+vue+echarts可视化查询所有CSDN用户质量分

文章目录 ⭐前言⭐ 效果⭐django简介⭐vue3简介⭐vue引入echarts ⭐前后分离实现&#x1f496; django代码层&#x1f496; vue3代码层结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于前后分离djangovueecharts可视化查询CSDN用户质量分。 该系列文章&#…

Spring IoC及DI依赖注入

Spring 1.Spring的含义&#xff1a; Spring 可从狭义与广义两个角度看待 狭义的 Spring 是指 Spring 框架(Spring Fremework) 广义的 Spring 是指 Spring 生态体系 2.狭义的 Spring 框架 Spring 框架是企业开发复杂性的一站式解决方案 Spring 框架的核心是 IoC 容器和 AO…

LayUi之选项卡的详解(附源码讲解)

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于LayUi的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.选项卡是什么 二.选项卡在什么时候使用…

java版鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统源代码

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…

【云原生】· 一文了解docker中的网络

目录 &#x1f352;查看docker网络 &#x1f352;bridge网络 &#x1f352;none网络 &#x1f352;host网络 &#x1f352;自定义容器网络 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f990;专栏地址&#xff1a;云原生专栏 根据前面的学习&#xff0c;已经对d…

Qt/C++原创项目作品精选(祖传原创/性能凶残)

00 前言说明 从事Qt开发十年有余&#xff0c;一开始是做C#.NET开发的&#xff0c;因为项目需要&#xff0c;转行做嵌入式linux开发&#xff0c;在嵌入式linux上做可视化界面开发一般首选Qt&#xff0c;当然现在可选的方案很多比如安卓&#xff0c;但是十多年前那时候板子性能低…

Heroku 部署有关 opencv 的 Django 后端应用(pdf2docx)

文章目录 场景解决方案AptfileBuildpacksDashboard 上手动构建Heroku CLI 终端构建 Requirements.txt提交改变 场景 我使用 heroku 部署了一个 Django 后端项目&#xff0c;里面使用了 pdf2docx 库进行 pdf 转 docx 这个库底层调用了 opencv 的一些操作&#xff0c;因此在部署…

服务器反向代理

反向代理作用 隐藏服务器信息 -> 保证内网的安全&#xff0c;通常将反向代理作为公网访问地址&#xff0c;web服务器是内网&#xff0c;即通过nginx配置外网访问web服务器内网 举例 百度的网址是&#xff1a;http://www.baidu.com &#xff0c; 现在我通过自己的服务器地…

手机自动化脚本-- 模拟器模拟真机环境过检测

文章目录 安装包目录结构一、安装雷电模拟器9二、新建模拟器并且初始化1. 性能设置2. 其他设置3. 当前配置三、面具1. 安装2. 面具初始化四、修改机型1. 将机型模块安装到模拟器2. 安装机型模块五、修改su问题1. 安装RE管理器2. 删除自带su文件,避免文件冲突六、下载地址额外安…

Linux中使用nmap基本使用以及扫描一组计算机的脚本

nmap扫描一组计算机的脚本 说明 Nmap&#xff08;Network Mapper&#xff09;是一个开源的网络探测和安全评估工具。它用于扫描和发现网络上的主机、端口和服务&#xff0c;以及评估网络的安全性。 Nmap具有以下主要功能&#xff1a; 主机发现&#xff1a;Nmap可以通过发送…

unity 调用C++ dll 有类和指针操作

这个在之前unity 调用C dll 操作升级套娃函数调用_天人合一peng的博客-CSDN博客的基础上&#xff0c;但实事时类相互嵌套&#xff0c;非常不好处理。 1 测试直接将main()生成dll程序能运行不。 发现是可以的。 2 那就是想方法把对象或指针的操作的下一级函数直接写到main里面&…

Linux 学习总结(93)—— 实用 shell 脚本汇总

1、检测两台服务器指定目录下的文件一致性 #!/bin/bash ###################################### 检测两台服务器指定目录下的文件一致性 ##################################### #通过对比两台服务器上文件的md5值&#xff0c;达到检测一致性的目的 dir/data/web …

STM32基础知识点总结

一、基础知识点 1、课程体系介绍 单片机概述arm体系结构STM32开发环境搭建 STM32-GPIO编程-点亮世界的那盏灯 STM32-USART串口应用SPI液晶屏 STM32-中断系统 STM32-时钟系统 STM32-ADC DMA 温湿度传感器-DHT11 2.如何学习单片机课程 多听理论、多理解、有问题及时提问 自己多…

ChatGPT助力校招----面试问题分享(十一)

1 ChatGPT每日一题&#xff1a;PCB布线&#xff0c;高速信号线走直角的后果 问题&#xff1a;PCB布线&#xff0c;高速信号线走直角的后果 ChatGPT&#xff1a;对于高速信号线来说&#xff0c;最好避免使用直角布线。直角布线会引入反射和信号损耗&#xff0c;从而导致信号完…

【Python】selenium项目实战:从12306网站获取特定时间段二等座有票的车次

文章目录 一、项目背景二、页面查找1、查询条件2、定位有二等座的元素3、定位有二等座的车次信息4、CtrlF检验xpath查找的车次 三、代码实现 一、项目背景 工具&#xff1a; pythonpycharmselenium 12306网址&#xff1a; https://kyfw.12306.cn/otn/leftTicket/init?linktyp…

【云原生】Docker跨主机网络Overlay与Macvlan的区别

跨主机网络通信解决方案 docker原生的overlay和macvlan 第三方的flannel&#xff0c;weave&#xff0c;calico 1.overlay网络 在Docker中&#xff0c;Overlay网络是一种容器网络驱动程序&#xff0c;它允许在多个Docker主机上创建一个虚拟网络&#xff0c;使得容器可以通过这…

氢辉能源|[4GW]质子交换膜产线投产发布会暨[3MW]PEM电解槽正式交付

2023年7月12日下午&#xff0c;氢辉能源&#xff08;深圳&#xff09;有限公司&#xff08;以下简称氢辉能源&#xff09;质子交换膜产线投产发布会暨12台50标方3MW电解槽交付仪式在深圳市龙岗区国际低碳城成功举办。 此外&#xff0c;氢辉能源与远景能源、润世华集团、宏洲新能…