Skip to content

You can not use .onDelete("cascade") with a model that has a composite primary key. #1620

@tintin10q

Description

@tintin10q

Environment

  • Operating System: Linux
  • Node Version: v18.12.1

Reproduction

See #1619

This is the schema:

import { Model } from "pinia-orm";

export class User extends Model {
  static entity = "user" as const;

  static fields() {
    return {
      id: this.uid(),
      name: this.string(""),
      episodes: this.hasMany(UserSavedEpisode, "user_id").onDelete("cascade"),
    };
  }

  declare id: number;
  declare name: string;
  declare episodes: UserSavedEpisode;
}

export class UserSavedEpisode extends Model {
  static entity = "savedEpisode";
  static primaryKey = ["user_id", "episode_id"];
  // static primaryKey = "episode_id" // change the primary key to a non-composite and delete user works

  static fields() {
    return {
      user_id: this.uid(),
      episode_id: this.uid(),
      human: this.belongsTo(User, "user_id"),
    };
  }

  declare user_id: string;
  declare episode_id: string;
}

Here is vue code to reproduce the error:

<script setup lang="ts">
import { computed } from "vue";

import { useRepo } from "pinia-orm";
import { User, UserSavedEpisode } from "/src/bugreport";

const user = useRepo(User);
const usersavedepisode = useRepo(UserSavedEpisode);

function clear() {
  user.flush();
  usersavedepisode.flush();
}

function save_user() {
  user.save({
    id: "userid",
    name: "tintin10q",
  });
}

function insert_episode() {
  usersavedepisode.save({
    user_id: "userid",
    episode_id: "episode_id",
  });
}

function delete_user() {
  user.destroy("userid");
}

const theuser = computed(() => user.query().whereId("userid").with("episodes").first());
const all_episodes = computed(() => usersavedepisode.all());
</script>

<template>
  <div style="display: flex; flex-direction: column; gap:.25rem">
    <button @click="save_user()">Create user</button>
    <button @click="insert_episode()">Give user episode</button>
    <button @click="delete_user()">Delete User</button>
    <button @click="clear()">Clear</button>
  </div>

  <h2>User</h2>
  <span> User: {{ theuser }} </span>

  <h2>Episodes</h2>
  <div v-for="i in all_episodes">{{ i }}</div>
</template>
  1. Click create user
  2. Click give user episode
  3. Click delete episode and observe the error
  4. Make the primary key of saved episode a single primary key. (uncomment line 22 and comment out line 21)
  5. Observe that you don't get an error now

Describe the bug

You can not use .onDelete("cascade") if you have a model with a composite primary key.

I get this error:

Uncaught (in promise) Error: [Pinia ORM] Please provide the local key for the relationship. The model with the composite key can't infer its local key.

Additional context

With a composite primary key it makes sense that when you are cascading a delete you don't have all the values for the other parts of the primary key. This means that indeed, you can not cascade the delete based on deleting with the primary key. So if you delete a user and want to cascade the delete you do not know all the episode ids required to delete all the usersavedepisode by primary key.

But you could still simply perform the delete by doing the equivalent of usersavedepisode.query().where('user_id','user_id').delete(). This way you can still cascade the delete anyway.

Another way could be to first query all the usersavedepisode with the specific user_id which we are trying to delete and then delete based on primary key with the user_id and the obtained primary keys. But to me that just sounds like double the amount of work and more complicated than the first solution.

Logs

Uncaught Error: [Pinia ORM] Please provide the local key for the relationship. The model with the composite key can't infer its local key.
    throwError pinia-orm.1cd11660.mjs:99
    assert pinia-orm.1cd11660.mjs:103
    $getLocalKey index.mjs:2791
    relationIds index.mjs:1198
    checkAndDeleteRelations index.mjs:1197
    dispatchDeleteHooks index.mjs:1236
    dispatchDeleteHooks index.mjs:1229
    destroyOne index.mjs:1153
    destroy index.mjs:1147
    destroy index.mjs:1598
    delete_user Test.vue:30

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions