跳转到主要内容

在Wagtail和Airtable之间同步数据

项目描述

Wagtail/Airtable

Wagtail的一个扩展,允许内容在Airtable表格和您的Wagtail/Django模型之间传输。

Torchbox开发并由The Motley Fool赞助。

Wagtail Airtable demo

工作原理

当您设置一个模型以“映射”到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_airtablewagtail.snippets之上您的INSTALLED_APPS
  • 在您的设置中,您需要将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 表格中的数据就不会意外被覆盖。数据难以恢复,此选项有助于防止意外数据丢失。

示例基本配置

以下是基本配置或 ModelNameOtherModelName(两者都是已注册的 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_savepre_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=Truenull=True设置的场合,在序列化器上设置required=False也很重要。

在某些情况下,两个模型可能会获得相同的Airtable ID。为了解决导入过程中的此类错误,第一个找到的模型将被设置为“真实”的模型,而“冒充者”将被设置为"" - 如果这种情况经常发生,可能会在您的系统中创建重复的模型。请确保您的导出方法和序列化导入设置正确。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

wagtail_airtable-0.7.0.tar.gz (32.4 kB 查看哈希值)

上传时间

构建分布

wagtail_airtable-0.7.0-py3-none-any.whl (32.3 kB 查看哈希值)

上传时间 Python 3

由以下支持