多对多关系中的缓存更新

Posted

技术标签:

【中文标题】多对多关系中的缓存更新【英文标题】:Cache update in a many 2 many relation 【发布时间】:2020-05-12 18:15:52 【问题描述】:

我在我的项目中使用了 Django、GraphQL (graphene-django)、VueJS 和 Apollo Client 的组合。在我尝试管理 many2many 关系之前,一切正常。通常,当我对已经在缓存中的对象进行更新突变时,会检测到它并更新显示的数据。 问题是,在我的新案例中,我正在更新我的两个对象之间的 many2many 关系并且它没有更新,而它在数据库中正确完成(甚至在缓存中,当我在控制台中打印它时,它是诡异的)。通常,我试图在组中/从组中添加/删除用户。也许我已经看我的代码太久了,我看不到明显的东西。无论如何这是我的代码:

我的 Django 模型:

class Group(models.Model):

  def _get_users_count(self):
      return self.user_ids.count()

  name = models.CharField(max_length=255)
  user_ids = models.ManyToManyField(User, related_name="group_ids", db_table="base_group_user")
  users_count = property(fget=_get_users_count, doc="type: Integer")

  def __str__(self):
      return self.name

class User(AbstractUser):

  def _get_full_name(self):
      return "%s %s" % (self.first_name, self.last_name.upper())

  full_name = property(fget=_get_full_name, doc="type: String")

  def __str__(self):
      return self.full_name

  def _has_group(self, group_name):
      return self.group_ids.filter(name=group_name).exists()

我的架构:

class GroupType(DjangoObjectType):

  class Meta:
      model = Group
      fields = ('id', 'name', 'user_ids',)

  users_count = graphene.Int()

class UserType(DjangoObjectType):

  class Meta:
      model = User
      fields = (
          'id',
          'username',
          'password',
          'first_name',
          'last_name',
          'is_active',
          'group_ids',
      )

  full_name = graphene.String()

我的变异器:

class AddUserInGroup(graphene.Mutation):
  id = graphene.ID()
  name = graphene.String()

  class Arguments:
    group_id = graphene.ID()
    user_id = graphene.ID()

  class Meta:
    output = GroupType

  def mutate(self, info, **kwargs):
      group = get_object_or_404(Group, id=kwargs['group_id'])
      user = get_object_or_404(User, id=kwargs['user_id'])
      group.user_ids.add(user)
      return group


class RemoveUserFromGroup(graphene.Mutation):
  id = graphene.ID()
  name = graphene.String()

  class Arguments:
    group_id = graphene.ID()
    user_id = graphene.ID()

  class Meta:
    output = GroupType

  def mutate(self, info, **kwargs):
    group = get_object_or_404(Group, id=kwargs['group_id'])
    user = get_object_or_404(User, id=kwargs['user_id'])
    group.user_ids.remove(user)
    return group

我的 js 常量(gql 查询):

const ADD_USER_IN_GROUP_MUTATION = gql`
  mutation addUserInGroupMutation ($groupId: ID!, $userId: ID!) 
    addUserInGroup(
      groupId: $groupId,
      userId: $userId
    ) 
      id
      name
      usersCount
      userIds 
        id
        username
      
    
  
`

const REMOVE_USER_FROM_GROUP_MUTATION = gql`
  mutation remoteUserFromGroupMutation ($groupId: ID!, $userId: ID!) 
    removeUserFromGroup(
      groupId: $groupId,
      userId: $userId
    ) 
      id
      name
      usersCount
      userIds 
        id
        username
      
    
  
`

const GROUPS_QUERY = gql`
  query 
    groups 
      id
      name
      usersCount
      userIds 
        id
        username
      
    
  
`

我的模板(样本):这是 2 列。当我删除一个用户时,它应该从左列中删除,它应该出现在不在组中的用户(右列)中,当我添加一个用户时,它应该从右列转到左列。

     <b-tab>
      <template v-slot:title>
        Users
        <b-badge class="ml-2"> group.usersCount </b-badge>
      </template>
      <b-row>
        <b-col>
          <b-table :items="group.userIds" :fields="table_fields_users_in">
            <template v-slot:cell(actionOut)="row">
              <b-button @click="removeUserFromGroup(row.item)" size="sm" variant="danger" title="Remove">
                <font-awesome-icon :icon="['fas', 'minus-circle']"/>
              </b-button>
            </template>
          </b-table>
        </b-col>
        <b-col>
          <b-table :items="users" :fields="table_fields_users_out">
            <template v-slot:cell(actionIn)="row">
              <b-button @click="addUserInGroup(row.item)" size="sm" variant="success" title="Add">
                <font-awesome-icon :icon="['fas', 'plus-circle']"/>
              </b-button>
            </template>
          </b-table>
        </b-col>
      </b-row>
    </b-tab>

我的数据:(以便您了解它们的来源)。请注意,我应该将“用户”细化为尚未/不再在组中的用户。

props: 
 group: Object,
 editing: 
   type: Boolean,
   default: false
 
,
apollo:  users: USERS_QUERY ,

最后是我的方法

addUserInGroup (user) 
  this.$apollo.mutate(
    mutation: ADD_USER_IN_GROUP_MUTATION,
    variables: 
      groupId: this.group.id,
      userId: user.id
    ,
    update: (cache,  data:  addUserInGroup  ) => 
      console.log('cache : ') // currently used for debugging, will be removed
      console.log(cache) // currently used for debugging, will be removed
      console.log('query answer: ') // currently used for debugging, will be removed
      console.log(addUserInGroup) // currently used for debugging, will be removed
    
  )
  console.log('Added ' + user.username + ' to ' + this.group.name + ' group.')
,
removeUserFromGroup (user) 
  this.$apollo.mutate(
    mutation: REMOVE_USER_FROM_GROUP_MUTATION,
    variables: 
      groupId: this.group.id,
      userId: user.id
    ,
    update: (cache,  data:  removeUserFromGroup  ) => 
      console.log('cache : ') // currently used for debugging, will be removed
      console.log(cache)  // currently used for debugging, will be removed
      console.log('query answer : ')  // currently used for debugging, will be removed
      console.log(removeUserFromGroup)  // currently used for debugging, will be removed
    
  )
  console.log('Removed ' + user.username + ' from ' + this.group.name + ' group.')

如果你们需要更多代码(有些部分我可能已经忘记了),请随时告诉我,我会提供。

我正在做的一个示例(这样您会注意到我的演示数据实际上是通过砸键盘制作的):

【问题讨论】:

如果缓存已修改,问题在于组件/视图更新 - 强制查询从缓存中重新获取/读取? - 不知道在 vue 中如何 - 我正在使用 react 我认为在更新已知类型和现有 ID 的对象时(这就是这里默认的缓存机制,它适用于没有 m2m 关系管理的简单模型),它会自行更新。我会更深入地寻找一种手动重新渲染的方法,即使我更愿意避免这样做。我会及时通知您我的研究。 分页/获取更多示例应包含此机制 在这种情况下缓存应该自动更新是对的。您最初用于获取组的查询是什么? 只能查询缓存 【参考方案1】:

最后,对于我的用例,我注意到由于数据(在数据库中)有很多潜在的变化,缓存并不是最相关或最可靠的数据源。 因此,我将默认的 apollo fetchPolicy 更改为 'network-only'

const apolloProvider = new VueApollo(
  defaultClient: apolloClient,
  defaultOptions: 
    $query: 
      fetchPolicy: 'network-only'
    
  
)

正如记录的HERE 和解释的HERE。

因此,每次我挂载具有 apollo 语句的组件时,它都会从网络(从数据库)获取查询。 我必须进行缓存更改的唯一剩余时刻是当我希望当前渲染的组件动态更新某些元素时。

【讨论】:

以上是关于多对多关系中的缓存更新的主要内容,如果未能解决你的问题,请参考以下文章

更新多对多关系中的实体

如何更新多对多关系中的现有对象(.Net 5)

多对多关系 - 如何更新数据透视表中的属性

框架 day32 Hibernate,一级缓存,关联关系映射(一对多,多对多)

如何在多对多关系中更新日期

反对 js:无法使用连接表额外属性更新多对多关系。说附加属性无效