在Wagtail和Airtable之间同步数据
项目描述
Wagtail/Airtable
Wagtail的一个扩展,允许内容在Airtable表格和您的Wagtail/Django模型之间传输。
由Torchbox开发并由The Motley Fool赞助。
工作原理
当您设置一个模型以“映射”到Airtable表格时,每次保存模型,它都会尝试更新Airtable中的行。如果找不到行,它将在您的Airtable中创建一个新行。
当您想将Airtable数据同步到您的Wagtail网站时,您可以转到“设置 -> Airtable导入”。然后,您可以通过单击按钮将整个表导入到您的Wagtail实例中。如果您看到“{您的模型}未设置正确的Airtable设置”,您需要仔细检查您的设置。默认情况下,导入页面可以在http://yourwebsite.com/admin/airtable-import/找到,或者如果您使用自定义/admin/ URL,它将是http://yourwebsite.com/{custom_admin_url}/airtable-import/。
幕后...
此包将尝试使用record_id
将模型对象与Airtable中的行匹配。如果一个模型没有record_id值,它将使用AIRTABLE_UNIQUE_IDENTIFIER
来尝试将Airtable中的唯一值与模型中的唯一值匹配。如果成功,您的模型对象将与Airtable中的行“配对”。但如果记录搜索失败,在保存模型时将创建Airtable中的新行,或者尝试在从Airtable导入模型时创建新模型对象。
注意:从Airtable导入时,对象创建可能会失败。这是预期行为,因为Airtable可能没有模型所需的所有数据。例如,Wagtail页面使用django-treebeard,路径是一个必需的字段。如果页面模型导入设置不包括路径字段,或者Airtable中没有路径列,则无法创建页面。此规则适用于Django模型上的任何必需字段,包括Wagtail页面上的其他必需字段。
安装与配置
-
使用
pip install wagtail-airtable
安装包 -
将
'wagtail_airtable'
添加到项目的INSTALLED_APPS
。- 在Wagtail 5.x中,为了在片段列表视图中启用特定于片段的导入按钮,请确保
wagtail_airtable
在wagtail.snippets
之上您的INSTALLED_APPS
中
- 在Wagtail 5.x中,为了在片段列表视图中启用特定于片段的导入按钮,请确保
-
在您的设置中,您需要将Django模型映射到Airtable设置。您想要映射到Airtable表格的每个模型都需要以下内容:
AIRTABLE_BASE_KEY
。您可以在登录到Airtable.com时在Airtable API文档中找到基础键AIRTABLE_TABLE_NAME
以确定要连接的表。AIRTABLE_UNIQUE_IDENTIFIER
。这可以是字符串或将Airtable列名称映射到您的模型中唯一字段的字典。- 例如:
AIRTABLE_UNIQUE_IDENTIFIER: 'slug',
这会将模型中的slug
字段与Airtable中的slug
列名称匹配。如果您的模型字段和Airtable列名称相同,请使用此选项。 - 例如:
AIRTABLE_UNIQUE_IDENTIFIER: {'Airtable Column Name': 'model_field_name'},
这会将Airtable Column Name
映射到名为model_field_name
的模型字段。如果您的Airtable列名称和模型字段名称不同,请使用此选项。
- 例如:
AIRTABLE_SERIALIZER
,它接受指向您的序列化器的字符串路径。这有助于将来自Airtable的数据映射到您的模型字段。需要Django Rest Framework。请参阅examples/目录中的序列化器示例。
-
最后,请确保您已启用 wagtail-airtable,方法是在
WAGTAIL_AIRTABLE_ENABLED = True
中设置。默认情况下,此选项是禁用的,这样您的 Wagtail 网站和 Airtable 表格中的数据就不会意外被覆盖。数据难以恢复,此选项有助于防止意外数据丢失。
示例基本配置
以下是基本配置或 ModelName
和 OtherModelName
(两者都是已注册的 Wagtail 片段)以及 HomePage
。
# your settings.py
AIRTABLE_API_KEY = 'yourSuperSecretKey'
WAGTAIL_AIRTABLE_ENABLED = True
AIRTABLE_IMPORT_SETTINGS = {
'appname.ModelName': {
'AIRTABLE_BASE_KEY': 'app3ds912jFam032S',
'AIRTABLE_TABLE_NAME': 'Your Airtable Table Name',
'AIRTABLE_UNIQUE_IDENTIFIER': 'slug', # Must match the Airtable Column name
'AIRTABLE_SERIALIZER': 'path.to.your.model.serializer.CustomModelSerializer'
},
'appname.OtherModelName': {
'AIRTABLE_BASE_KEY': 'app4ds902jFam035S',
'AIRTABLE_TABLE_NAME': 'Your Airtable Table Name',
'AIRTABLE_UNIQUE_IDENTIFIER': {
'Page Slug': 'slug', # 'Page Slug' column name in Airtable, 'slug' field name in Wagtail.
},
'AIRTABLE_SERIALIZER': 'path.to.your.model.serializer.OtherCustomModelSerializer'
},
'pages.HomePage': {
'AIRTABLE_BASE_KEY': 'app2ds123jP23035Z',
'AIRTABLE_TABLE_NAME': 'Wagtail Page Tracking Table',
'AIRTABLE_UNIQUE_IDENTIFIER': {
'Wagtail Page ID': 'pk',
},
'AIRTABLE_SERIALIZER': 'path.to.your.pages.serializer.PageSerializer',
# Below are OPTIONAL settings.
# By disabling `AIRTABLE_IMPORT_ALLOWED` you can prevent Airtable imports
# Use cases may be:
# - disabling page imports since they are difficult to setup and maintain,
# - one-way sync to Airtable only (ie. when a model/Page is saved)
# Default is True
'AIRTABLE_IMPORT_ALLOWED': True,
# Add the AIRTABLE_BASE_URL setting if you would like to provide a nice link
# to the Airtable Record after a snippet or Page has been saved.
# To get this URL open your Airtable base on Airtable.com and paste the link.
# The recordId will be automatically added so please don't add that
# You can add the below setting. This is optional and disabled by default.
'AIRTABLE_BASE_URL': 'https://airtable.com/tblxXxXxXxXxXxXx/viwxXxXxXxXxXxXx',
# The PARENT_PAGE_ID setting is used for creating new Airtable Pages. Every
# Wagtail Page requires a "parent" page. This setting can either be:
# 1. A callable (ie `my_function` without the parentheses)'
# Example:
# 'PARENT_PAGE_ID': custom_function,
# 2. A path to a function. (ie. 'appname.directory.filename.my_function')
# Example:
# 'PARENT_PAGE_ID': 'path.to.function',
# 3. A raw integer.
# Example:
# 'PARENT_PAGE_ID': 3,
# If you choose option #1 (callable) or option #2 (path to a function)
# Your function needs to return an integer which will represent the Parent
# Page ID where all imported pages will be created as child pages.
# Callables and path-to-functions (option #1 and option #2 in the above docs)
# Take an `instance` kwarg as of v0.2.1. Example below:
# def custom_parent_page_id_function(instance=None):
# if instance and isinstance(instance, Page):
# return Page.objects.get(pk=instance.id).get_parent()
'PARENT_PAGE_ID': 'path.to.function',
# The `AUTO_PUBLISH_NEW_PAGES` setting will tell this package to either
# Automatically publish a newly created page, or set to draft.
# True = auto publishing is on. False = auto publish is off (pages will be drafts)
'AUTO_PUBLISH_NEW_PAGES': False,
},
# ...
}
有多个具有相同 Airtable 设置的模型吗?
最常见的方法可能是支持少数模型,在这种情况下,使用以下示例将更快更干净。只需编写一次配置字典,以防止配置膨胀。
AIRTABLE_API_KEY = 'yourSuperSecretKey'
WAGTAIL_AIRTABLE_ENABLED = True
CUSTOM_PAGE_SETTINGS = {
'AIRTABLE_BASE_KEY': 'app3ds912jFam032S',
'AIRTABLE_TABLE_NAME': 'Your Airtable Table Name',
'AIRTABLE_UNIQUE_IDENTIFIER': 'slug', # Must match the Airtable Column name
'AIRTABLE_SERIALIZER': 'path.to.your.model.serializer.CustomModelSerializer'
},
AIRTABLE_IMPORT_SETTINGS = {
'home.HomePage': CUSTOM_PAGE_SETTINGS,
'blog.BlogPage': CUSTOM_PAGE_SETTINGS,
'appname.YourModel': CUSTOM_PAGE_SETTINGS,
}
在 Airtable 导入时创建 Wagtail 页面
此功能由 Mozilla 基金会 赞助。
从 wagtail-airtable
v0.1.6 版本开始,您可以从 Airtable 导入创建 Wagtail 页面。
需要设置一些设置才能使其按预期工作。阅读以下代码以查看需要哪些设置
AIRTABLE_IMPORT_SETTINGS = {
'pages.HomePage': {
'AIRTABLE_BASE_KEY': 'app2ds123jP23035Z',
'AIRTABLE_TABLE_NAME': 'Wagtail Page Tracking Table',
'AIRTABLE_UNIQUE_IDENTIFIER': {
'Wagtail Page ID': 'pk',
},
'AIRTABLE_SERIALIZER': 'path.to.your.pages.serializer.PageSerializer',
'AIRTABLE_IMPORT_ALLOWED': True, # This must be set
'PARENT_PAGE_ID': 'path.to.function.that.returns.an.integer', # This must be set
},
}
设置就绪后,您可以在 Airtable 中开始创建新的页面,并通过 Wagtail Admin(位于设置菜单中)导入这些页面。
注意事项:在上面的代码中,我们看到 {'Wagtail Page ID': 'pk',}
,这意味着 Airtable 中有一个名为 "Wagtail Page ID" 的列,并将其映射到页面 pk。当您在 Airtable 表格内创建新的 Wagtail 页面时,在新行的此单元格中保持空白。当它被导入时将自动更新。这是因为 Airtable(以及编辑器)可能不知道新的页面 ID 将是什么,所以我们让 Wagtail 设置它,然后再次更新 Airtable。
钩子
钩子是在某个动作发生后执行代码的一种方式。这模仿(并在内部使用)Wagtail 的钩子功能。
注意:使用钩子会添加处理时间到您的请求。如果您使用 Heroku 且有 30 秒的超时,您可能希望使用管理命令以避免遇到服务器超时。
更新记录
要记录记录更新时采取的操作,您可以在 wagtail_hooks.py 文件中编写这样的钩子
@hooks.register('airtable_import_record_updated')
def airtable_record_updated(instance, is_wagtail_page, record_id):
# Instance is the page or model instance
# is_wagtail_page is a boolean to determine if the object is a wagtail page. This is a shortcut for `isinstance(instance, wagtail.models.Page)`
# record_id is the wagtail record ID. You can use this to perform additional actions against Airtable using the airtable-python-wrapper package.
pass
管理命令
python manage.py import_airtable appname.ModelName secondapp.SecondModel
您可以选择使用 --verbosity=2
标志提高详细程度以进行更好的调试。
import_airtable 命令
此命令将查找您提供的任何 appname.ModelName
,并使用映射设置在 Airtable 中查找数据。有关导入工作方式的更多信息,请参阅“幕后”部分。
跳过 Django 信号
默认情况下,import_airtable
命令在保存的模型中添加了一个额外的属性,称为 _skip_signals
,并将其设置为 True
。您可以使用此属性来绕过可能存在于导入模型上的任何 post_save
或 pre_save
信号,从而使这些信号不会运行。例如:
@receiver(post_save, sender=MyModel)
def post_save_function(sender, **kwargs):
if sender._skip_signals:
# rest of logic
如果您不在信号中执行这些检查,则保存将正常进行。
本地测试建议
注意:请小心不要使用生产设置,因为这可能会导致覆盖 Wagtail 或 Airtable 数据。
由于 Airtable 不提供测试环境,您需要针对实时表进行测试。最好的方法是将您的实时表复制到一个新表(重命名它可以帮助避免命名混淆),并更新您的本地设置。使用此方法,您可以在一个可丢弃的 Airtable 上安全地测试一切。如果某些东西被彻底破坏而无法修复,请删除测试表并重新复制原始表。
本地调试
由于连接Wagtail和Airtable的复杂性和脆弱性(因为Airtable的列可以是几乎任何值),您可能需要一些帮助来调试您的设置。要开启更高的输出详细程度,您可以通过启用Airtable的调试设置WAGTAIL_AIRTABLE_DEBUG = True
。这只会增加运行管理命令时的默认详细程度。在一个标准的Django管理命令中,您可以运行python manage.py import_airtable appname.ModelName --verbosity=2
,然而,当您使用Wagtail管理员导入页面从Airtable导入时,您将无法访问此详细程度参数。但是,通过启用WAGTAIL_AIRTABLE_DEBUG
,您可以手动增加详细程度。
注意:此操作仅在您的设置中
DEBUG = True
时有效,以避免意外地使您的生产日志溢出。
Airtable最佳实践
Airtable的列可以是多种“类型”之一,非常类似于Python数据类型或Django字段。您可以拥有电子邮件列、URL列、单行文本、复选框等。
为了帮助在您的Django/Wagtail实例和Airtable Base之间维护适当的数据同步,您应该将列类型设置为尽可能接近您的Django字段。
例如,如果您在Django模型(或Wagtail页面)中有一个BooleanField,并且您想支持将数据推送到Airtable以及从Airtable导入相同的数据,您应该将Airtable中的列类型设置为复选框(因为它的状态只能是开/关,类似于BooleanField只能是True/False)。
在其他情况下,例如Airtable的电话号码列类型:如果您正在使用第三方包来处理电话号码和电话号码验证,您将需要编写一个自定义序列化器来处理从Airtable(当您从Airtable导入时)传入的值。数据可能以字符串的形式进入Wagtail,并且您可能需要调整字符串值以符合内部Wagtail/Django存储的电话号码格式。(您也可能需要在导出到Airtable时将电话号码转换为标准的字符串格式)
运行测试
克隆项目并进入wagtail-airtable/
目录。然后运行python runtests.py tests
。该项目使用标准的Django单元测试。
要针对特定的测试运行,您可以运行python runtests.py tests.test_file.TheTestClass.test_specific_model
测试是针对Wagtail 2.10及以后的版本编写的。
自定义保存方法
在某些情况下,您可能想自定义保存的方式,例如使Airtable的保存异步进行。
要这样做,请在您的settings.py文件中设置:WAGTAIL_AIRTABLE_SAVE_SYNC=False。
这将退出原始的保存方法,并且您需要自行启用此处的异步部分。
以下是如何使用信号after_page_publish
以及django_rq设置此配置的示例。
#settings.py
WAGTAIL_AIRTABLE_SAVE_SYNC=False
WAGTAIL_AIRTABLE_PUSH_MESSAGE="Airtable save happening in background"
#wagtail_hooks.py
from django.dispatch import receiver
from wagtail.models import Page
@job('airtable')
def async_airtable_save(page_id):
my_page = Page.objects.get(page_id).specific
my_page.save_to_airtable()
@receiver('page_published')
def upload_page_to_airtable(request, page):
async_airtable_save.delay(page.pk)
如果这样做,消息将会关闭,因此已经提供了一些设置,您可以将其更改为任何您想要的消息:WAGTAIL_AIRTABLE_PUSH_MESSAGE
- 将其设置为任何您想要的消息,例如:WAGTAIL_AIRTABLE_PUSH_MESSAGE='Airtable保存正在后台进行'
将导入操作添加到片段列表视图(Wagtail 6.x)
截至Wagtail 6.0,导入操作不再自动显示在片段列表视图中(尽管它仍然可以通过设置 -> Airtable导入访问)。要重新添加,首先确保您的片段模型已使用显式视图集注册,然后确保该视图集的索引视图继承自SnippetImportActionMixin
。
from wagtail.snippets.models import register_snippet
from wagtail.snippets.views.snippets import IndexView, SnippetViewSet
from wagtail_airtable.mixins import SnippetImportActionMixin
from .models import Advert
class AdvertIndexView(SnippetImportActionMixin, IndexView):
pass
class AdvertViewSet(SnippetViewSet):
model = Advert
index_view_class = AdvertIndexView
register_snippet(Advert, viewset=AdvertViewSet)
故障排除技巧
导入时发生重复
确保您的序列化器与您的字段定义完全匹配,在CharField
具有blank=True
或null=True
设置的场合,在序列化器上设置required=False
也很重要。
在某些情况下,两个模型可能会获得相同的Airtable ID。为了解决导入过程中的此类错误,第一个找到的模型将被设置为“真实”的模型,而“冒充者”将被设置为""
- 如果这种情况经常发生,可能会在您的系统中创建重复的模型。请确保您的导出方法和序列化导入设置正确。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。