TwistingTwists

TwistingTwists

I have a quiz. and a question.
and relation is.

quiz many_to_many question
question many_to_many quiz
via quiz_question

Now, I want to have an api where I can write Quiz.update_quiz_with_question_ids(existing_quiz,[ qid_1,qid_2,qid_3]) which ends up relating question_ids and quiz_ids.

What I tried:


    update :update_quiz_with_question_ids do
      accept []

      argument :question_ids, {:array, :uuid} do
        allow_nil? false
      end

    change manage_relationship(:questions, :question_ids, type: :direct_control)
    end

which leaves with the following error

{:error,
 %Ash.Error.Invalid{
   errors: [
     %Ash.Error.Query.Required{
       field: :question_id,
       type: :argument,
       resource: PyqRatta.Databank.Question,
       changeset: nil,
       query: nil,
       error_context: [],
       vars: [],
       path: [],
       stacktrace: #Stacktrace<>,
       class: :invalid
     }
   ],
...

So, I tried a little differently.

update :update_quiz_with_question_ids do
      accept []

      argument :question_ids, {:array, :uuid} do
        allow_nil? false
      end

    change manage_relationship(:questions, :question_ids,            
               on_match: :ignore,
               on_lookup: :relate,
               on_no_match: :relate,
               # since we are adding more questions to the quiz.
               on_missing: :ignore)
    end

and same error persists. Finally, did one more tweak.

defmodule Databank.Changes.QuestionsFromQuestionIds do
  @moduledoc """
  Fetches questions from database to udpate the relationship from questions to quizzes
  """
  use Ash.Resource.Change

  alias  Databank.Quiz
  alias Databank.Question
  alias  Databank.QuizQuestion

  def change(changeset, opts, context) do
    quiz_id = Ash.Changeset.get_data(changeset, :id)
    qids = Ash.Changeset.get_argument(changeset, :question_ids)

    questions =
      Enum.reduce(qids, [], fn qid, acc ->
        {:ok, question} = Question.read(qid)
        acc ++ [question]
      end)

   Ash.Changeset.force_set_argument(changeset, :question_ids, questions)
  
   
  end
end

along with


    update :update_quiz_with_question_ids do
      accept []

      argument :question_ids, {:array, :uuid} do
        allow_nil? false
      end

      change Databank.Changes.QuestionsFromQuestionIds

      change manage_relationship(:question_ids, :questions, type: :direct_control)
    end

the changeset is :

#Ash.Changeset<
     api: Databank,
     action_type: :update,
     action: :update_quiz_with_question_id,
     attributes: %{},
     relationships: %{
       questions: [
         {[%{id: "39f41254-f959-4f1d-96d4-30500d96d8d1"}],
          [
            ignore?: false,
            on_missing: :destroy,
            on_match: :update,
            on_lookup: :ignore,
            on_no_match: :create,
            eager_validate_with: false,
            authorize?: true,
            meta: [inputs_was_list?: false, id: :question_id],
            type: :direct_control
          ]}
       ]
     },
     arguments: %{question_id: "39f41254-f959-4f1d-96d4-30500d96d8d1"},
     errors: [
       %Ash.Error.Invalid{
         errors: [
           %Ash.Error.Query.Required{
             field: :question_id,
             type: :argument,
             resource: Databank.Question,
             changeset: nil,
             query: nil,
             error_context: [],
             vars: [],
             path: [],
             stacktrace: #Stacktrace<>,
             class: :invalid
           }
         ],
         stacktraces?: true,
         changeset: nil,
         query: #Ash.Query<
           resource: Databank.Quiz,
           load: [questions: []],
           errors: [
             %Ash.Error.Invalid{
               errors: [
                 %Ash.Error.Query.Required{
                   field: :question_id,
                   type: :argument,
                   resource: Databank.Question,
                   changeset: nil,
                   query: nil,
                   error_context: [],
                   vars: [],
                   path: [],
                   stacktrace: #Stacktrace<>,
                   class: :invalid
                 }
               ],
               stacktraces?: true,
               changeset: nil,
               query: #Ash.Query<
                 resource: Databank.Question,
                 filter: #Ash.Filter<id == nil>,
                 errors: [
                   %Ash.Error.Query.Required{
                     field: :question_id,
                     type: :argument,
                     resource: Databank.Question,
                     changeset: nil,
                     query: nil,
                     error_context: [],
                     vars: [],
                     path: [],
                     stacktrace: #Stacktrace<>,
                     class: :invalid
                   }
                 ],
                 select: [:id, :question_text, :question_image, :type,
                  :correct_answer_text, :correct_answer_image,
                  :explanation_text, :explanation_image, :short_description,
                  :long_description, :year, :tags, :created_at, :updated_at]
               >,
               error_context: [],
               vars: [],
               path: [],
               stacktrace: #Stacktrace<>,
               class: :invalid
             },
             %Ash.Error.Query.Required{
               field: :question_id,
               type: :argument,
               resource: Databank.Question,
               changeset: nil,
               query: nil,
               error_context: [],
               vars: [],
               path: [],
               stacktrace: #Stacktrace<>,
               class: :invalid
             }
           ]
         >,
         error_context: [nil],
         vars: [],
         path: [],
         stacktrace: #Stacktrace<>,
         class: :invalid
       },
...

The reasoning behind this is : if manage_relationship can alter the fields via given relationship key. But that doesn’t appear to be the case for many to many

TL;DR

create an Api where
relation is.

quiz many_to_many question
question many_to_many quiz
via quiz_question

Quiz.update_quiz_with_question_ids(existing_quiz,[ qid_1,qid_2,qid_3]) which ends up relating question_ids and quiz_ids.

Showing Posts 1 to 10

zachdaniel

zachdaniel

Creator of Ash

type: :append_and_remove. :direct_control will attempt to create, destroy and update questions to make the relationship look exactly like the provided values. So any given new value that is missing has to be enough input to call the corresponding create action. type: :append_and_remove will attempt to relate and unrelate existing questions.

TwistingTwists

TwistingTwists OP

 update :update_quiz_with_question_id do
      accept []

      argument :question_id, :uuid do
        allow_nil? false
      end

      change manage_relationship(:question_id, :questions, type: :append_and_remove)
    end

this doesn’t work either. same error. I can put a small demo repo, if you’d like

zachdaniel

zachdaniel

Creator of Ash

Ah, so the required argument bit is because arguments are required to be provided when the changeset is built. If you have an argument with allow_nil? false, you can’t set it in a change to avoid the error.

zachdaniel

zachdaniel

Creator of Ash

You should be able to do

argument :question_ids, {:array, :uuid} do
  allow_nil? false
end

change manage_relationship(:question_ids, :questions, type: :append_and_remove)

and then Ash.Changeset.for_....(%{question_ids: [....]})

If you just want them to add, and not remove ones they didn’t supply, then use type: :append

TwistingTwists

TwistingTwists OP

https://github.com/TwistingTwists/pyq_ratta/blob/d6b0b60e90df721f913c96bd6c9c79ea9062aba1/test/pyq_ratta/quiz_test.exs#L30

try running this test via

mix test test/pyq_ratta/quiz_test.exs:30 on branch feat/upload_questions

TwistingTwists

TwistingTwists OP

That makes sense.

https://github.com/TwistingTwists/pyq_ratta/blob/f6178f75322b4c544ce3678d0d41fabda7f15f1d/lib/pyq_ratta/databank/resources/quiz.ex#L82
However, even after trying that, tests fail. I suspect it is something else. :\

branch name is feat/upload_questions

zachdaniel

zachdaniel

Creator of Ash

I may have time to look tomorrow. Are you getting an error with that setup? Or how are your tests failing?

TwistingTwists

TwistingTwists OP

errors are:

{
              :error,
              %Ash.Error.Invalid{
                __exception__: true,
                changeset: #Ash.Changeset<api: PyqRatta.Databank, action_type: :update, action: :update_quiz_with_question_ids, attributes: %{}, relationships: %{questions: [{[%{id: "1085ee61-68b6-4bb8-ad7c-375b7129443f"}, %{id: "d86052e1-8feb-420b-92ce-17f5d35d9c07"}], [ignore?: false, on_missing: :ignore, on_match: :ignore, on_lookup: :relate, on_no_match: :error, eager_validate_with: false, authorize?: true, meta: [inputs_was_list?: true, id: :question_ids], type: :append]}]}, arguments: %{question_ids: ["1085ee61-68b6-4bb8-ad7c-375b7129443f", "d86052e1-8feb-420b-92ce-17f5d35d9c07"]}, errors: [%Ash.Error.Invalid{errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], stacktraces?: true, changeset: nil, query: #Ash.Query<resource: PyqRatta.Databank.Quiz, load: [questions: []], errors: [%Ash.Error.Invalid{errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], stacktraces?: true, changeset: nil, query: #Ash.Query<resource: PyqRatta.Databank.Question, filter: #Ash.Filter<id == nil>, errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], select: [:id, :question_text, :question_image, :type, :correct_answer_text, :correct_answer_image, :explanation_text, :explanation_image, :short_description, :long_description, :year, :tags, :created_at, :updated_at]>, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}, %Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}]>, error_context: [nil], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}, %Ash.Error.Invalid{errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], stacktraces?: true, changeset: nil, query: #Ash.Query<resource: PyqRatta.Databank.Question, filter: #Ash.Filter<id == nil>, errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], select: [:id, :question_text, :question_image, :type, :correct_answer_text, :correct_answer_image, :explanation_text, :explanation_image, :short_description, :long_description, :year, :tags, :created_at, :updated_at]>, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}, %Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], data: #PyqRatta.Databank.Quiz<questions: #Ash.NotLoaded<:relationship>, questions_join_assoc: #Ash.NotLoaded<:relationship>, __meta__: #Ecto.Schema.Metadata<:loaded, "quiz">, id: "457b3c38-0874-41e8-b725-96aa3631fb8b", short_description: nil, long_description: nil, year: nil, tags: [], created_at: ~U[2024-01-11 03:35:07.410725Z], updated_at: ~U[2024-01-11 03:35:07.410725Z], aggregates: %{}, calculations: %{}, ...>, context: %{actor: nil, authorize?: false}, valid?: true>,
                class: :invalid,
                error_context: [nil, nil, nil],
                errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}],
                path: [],
                query: #Ash.Query<resource: PyqRatta.Databank.Quiz, load: [questions: []], errors: [%Ash.Error.Invalid{errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], stacktraces?: true, changeset: nil, query: #Ash.Query<resource: PyqRatta.Databank.Question, filter: #Ash.Filter<id == nil>, errors: [%Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}], select: [:id, :question_text, :question_image, :type, :correct_answer_text, :correct_answer_image, :explanation_text, :explanation_image, :short_description, :long_description, :year, :tags, :created_at, :updated_at]>, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}, %Ash.Error.Query.Required{field: :question_id, type: :argument, resource: PyqRatta.Databank.Question, changeset: nil, query: nil, error_context: [], vars: [], path: [], stacktrace: #Stacktrace<>, class: :invalid}]>,
                stacktrace: #Stacktrace<>,
                stacktraces?: true,
                vars: []
              }
            }
     stacktrace:
       test/pyq_ratta/quiz_test.exs:41: (test)

error as Github Gist


just realised I had not pushed migrations. pushed now.

zachdaniel

zachdaniel

Creator of Ash

Weird I don’t see how you could be getting a required argument called :question_id when you don’t even have an argument anywhere in the question resource called :question_id.

zachdaniel

zachdaniel

Creator of Ash

Ah, never mind, you do :slight_smile:

This is the issue:

    read :read do
      argument :question_id, :uuid do
        allow_nil? false
      end

      # to indicate that only one record will be returned
      get? true
      primary? true

      filter expr(id == ^arg(:question_id))
    end

Your primary read action should not have required arguments. If it does, anything that tries to use it (like manage_relationship won’t know what to supply as the argument value.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews