Обновление кэша во многих 2 многих отношениях - PullRequest
2 голосов
/ 27 января 2020

Я использую комбинацию Django, GraphQL (graphene- django), VueJS и Apollo Client для моего проекта. Все работает нормально, пока я не попытался управлять многими отношениями. Обычно, когда я делаю мутацию обновления для объекта, который уже находится в кэше, он обнаруживается, и отображаемые данные обновляются. Проблема в том, что в моем новом случае я обновляю отношение 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 столбца. Когда я удаляю пользователя, он должен быть удален из левого столбца, и он должен появляться у пользователей, которых нет в группе (правый столбец), а когда я добавляю пользователя, он должен go из правого столбца слева один.

     <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.')
}

Если вам, ребята, нужно больше кода (некоторые части я бы забыл), не стесняйтесь, сообщите мне, и я предоставлю его.

Пример того, что я делаю (так что вы заметите, что мои демонстрационные данные буквально сделаны, разбивая мою клавиатуру):

enter image description here

1 Ответ

0 голосов
/ 28 февраля 2020

Наконец, для моего случая использования я заметил, что, поскольку существует много потенциальных изменений в данных (в базе данных), кэш не является самым актуальным или самым надежным источником данных. Поэтому я изменил свой apollo fetchPolicy по умолчанию на 'network-only'.

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

Как задокументировано ЗДЕСЬ и объяснил ЗДЕСЬ .

, чтобы каждый раз, когда я монтирую компонент, имеющий оператор apollo, он выбирает запрос из сети (из базы данных). Единственный оставшийся момент, когда мне нужно внести изменения в кэш, это когда я хочу, чтобы текущий визуализированный компонент динамически обновлял некоторые элементы.

...