【问题标题】:How to delete a photo from a database using Elixir and GraphQL如何使用 Elixir 和 GraphQL 从数据库中删除照片
【发布时间】:2018-07-12 11:13:51
【问题描述】:

我一直在关注由 EQuimper 制作的 Instagram 克隆教程。我一直在尝试通过后端进一步扩展应用程序,因为前端目前并不重要。我正在尝试实现删除照片功能,但它似乎不起作用,我不知道为什么。

mutation do
    @doc """
    deletes a photo in the database
    """
    field :photo_delete, list_of(:photo) do
        arg :id, non_null(:id)
        resolve &Resolvers.Posts.photo_delete/3
    end
end

我的 schema.ex 在突变部分下包含此代码。

def photos(_,_,_) do
    {:ok, Instagram.Posts.list_photos}
end

def photo(_, %{id: id}, _) do
    {:ok, Instagram.Posts.get_photo!(id)}
end

def photo_delete(_, %{id: id}, _) do
    photo_from_db = {:ok, Instagram.Posts.get_photo!(id)}
    {:ok, Instagram.Posts.delete_photo(photo_from_db)}
end

这是 resolver.posts 文件中的代码,用于返回列表或单张照片。

def delete_photo(%Photo{} = photo) do
Repo.delete(photo)
end

这是执行突变以从数据库中删除照片的代码,它接收照片结构并将其删除。

object :photo do
    field :id, non_null(:id)
    field :image_url, non_null(:string)
    field :caption, :string

    field :inserted_at, non_null(:string)
    field :update_at, non_null(:string)
end

这是定义照片架构的代码。

schema "photos" do
field :caption, :string
field :image_url, :string

timestamps()

end

@doc 错误 def changeset(%Photo{} = photo, attrs) 做 照片 |> 演员表(属性,[:image_url,:标题]) |> validate_required([:image_url]) 结束

此代码位于处理架构的 photo.ex 文件中(我认为)

 mutation {
  photo_delete(id: 1){
    id
 }
}

这是我为从数据库中删除查询而运行的突变。它返回一个错误说

"no function clause matching in Instagram.Posts.delete_photo/1"

从终端返回。 我做错了什么?以及我对本示例中的功能流程有什么不了解的地方。 视频系列链接:https://www.youtube.com/watch?v=AVQQF_J3Az0&list=PLzQWIQOqeUSOu74jGJMRH06gneM3wL82Z 进一步说明。

【问题讨论】:

    标签: elixir phoenix-framework graphql absinthe


    【解决方案1】:
    def photo_delete(_, %{id: id}, _) do
        photo_from_db = {:ok, Instagram.Posts.get_photo!(id)}
        {:ok, Instagram.Posts.delete_photo(photo_from_db)}
    end
    

    应该是这样的

    with {:ok, photo_from_db} <- Instagram.Posts.get_photo!(id) do
      Instagram.Posts.delete_photo(photo_from_db)
    else 
      {:error, error} ->
        {:error, error}
    end
    

    或类似的东西。

    另外,我现在编写了所有上下文函数来返回 :ok/:error 元组,所以一切都与 Absinthe 搭配得很好。总的来说,这似乎也是一种很好的做法。

    通过硬编码 :ok 条件,您没有正确处理失败的情况,只会抛出异常而不是返回有用的错误。

    您可以使用这种中间件来处理错误:

    defmodule ApiWeb.Middleware.ErrorMiddleware do
      require Logger
      alias Api.Error.AbsintheError
    
      def add_error_handling(spec) do
        fn res, config ->
          spec
          |> to_fun(res, config)
          |> exec_safely(res)
        end
      end
    
      defp to_fun({{module, function}, config}, res, _config) do
        fn -> apply(module, function, [res, config]) end
      end
    
      defp to_fun({module, config}, res, _config) do
        fn -> apply(module, :call, [res, config]) end
      end
    
      defp to_fun(module, res, config) when is_atom(module) do
        fn -> apply(module, :call, [res, config]) end
      end
    
      defp to_fun(fun, res, config) when is_function(fun, 2) do
        fn -> fun.(res, config) end
      end
    
      defp exec_safely(fun, res) do
        fun.()
        |> Map.update!(:errors, &Enum.map(&1, fn e -> AbsintheError.serialize(e) end))
      rescue
        err ->
          # TODO: https://authkit.atlassian.net/projects/AUT/issues/AUT-9
          Logger.error(Exception.format(:error, err, __STACKTRACE__))
    
          Absinthe.Resolution.put_result(
            res,
            {:error, %{code: :internal_server_error, message: "An internal server error has occured"}}
          )
      end
    end
    

    然后像这样构建错误结构

    defmodule Api.Error.NotFoundError do
      @type error_source ::
              :internal | :network
    
      @type t :: %__MODULE__{
              source: error_source,
              code: :not_found,
              message: String.t()
            }
    
      @enforce_keys [:source, :code, :message]
      defstruct [:source, :code, :message]
    
      @spec new(Keyword.t()) :: t
      def new(fields) do
        struct!(__MODULE__, fields)
      end
    end
    

    并像这样实现

    defprotocol Api.Error.AbsintheError do
      def serialize(err)
    end
    
    defimpl Api.Error.AbsintheError, for: Api.Error.NotFoundError do
      @doc """
      `serialize` takes our standard `%NotFoundError{}` struct and converts it
      into a regular map in order to make it play nice with `Absinthe`.  We then
      use `Absinthe` `middleware` to force the execution of serialize every time
      it gets passed a `%NotFoundError{}`.
      """
      def serialize(err) do
        %{source: err.source, code: err.code, message: err.message}
      end
    end
    
    

    【讨论】:

      【解决方案2】:

      找到答案

      field :delete_user, :user do
              arg :id, non_null(:id)
              resolve &Graphical.UserResolver.delete/2
          end
      

      这在您的 schema.ex 文件中

       def delete(%{id: id}, _info) do
          Accounts.get_user!(id)
          |> Accounts.delete_user
      end
      

      然后从模式文件中调用它,它将找到该 id 处的记录,然后将其通过管道传送到删除方法,从数据库中删除它

      【讨论】:

        【解决方案3】:

        我认为最有可能的罪魁祸首是 schema.ex 中函数调用正上方的行:

        photo_from_db = {:ok, Instagram.Posts.get_photo!(id)}
        

        你可能想要的是这个:

        {:ok, photo_from_db} = Instagram.Posts.get_photo!(id)
        

        这样您将向函数传递它所期望的 photo 结构,而不是 {:ok, %Photo{}}

        【讨论】:

        • 这与我解决问题的方法很接近。 def photo_delete(%{id: id}, _info) 做 Instagram.Posts.get_photo!(id) |> Instagram.Posts.delete_photo 结束
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-11
        • 2020-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-29
        相关资源
        最近更新 更多