Продолжение 5: TypeError в методах "многие ко многим" - PullRequest
0 голосов
/ 06 мая 2020

Я использую Sequelize 5.21.7 (в следующем. js приложении), чтобы попытаться установить связь «многие-ко-многим» между моделями изображений и тегов, но я получаю TypeErrors при попытке использовать imageInstance.addTag() и imageInstance.getTags() методы (вместе с другими методами _Tag[s]()).

// Tag.ts
import Sequelize, { DataTypes, Model } from 'sequelize';
import sequelize from '../instance';

class Tag extends Model {
  id!: string;

  title!: string;
}

Tag.init(
  {
    id: {
      type: DataTypes.UUID,
      defaultValue: Sequelize.UUIDV4,
      primaryKey: true,
    },
    title: {
      type: DataTypes.STRING,
      unique: true,
    },
  },
  {
    sequelize,
    modelName:'tag',
  },
);


export default Tag;
// Image.ts
import Sequelize, {
  DataTypes,
  BelongsToManyAddAssociationMixin,
  BelongsToManyCountAssociationsMixin,
  BelongsToManyCreateAssociationMixin,
  BelongsToManyGetAssociationsMixin,
  BelongsToManyHasAssociationMixin,
  BelongsToManyRemoveAssociationMixin,
  BelongsToManySetAssociationsMixin,
  Model,
  Association,
} from 'sequelize';
import sequelize from '../instance';
import { Tag } from '../index';

class Image extends Model {
  id!: string;

  fileName!: string;

  title: string;

  Tags: Tag[];

  public getTags!: BelongsToManyGetAssociationsMixin<Tag>;
  public addTag!: BelongsToManyAddAssociationMixin<Tag, number>;
  public setTags!: BelongsToManySetAssociationsMixin<Tag, number>;
  public hasTag!: BelongsToManyHasAssociationMixin<Tag, number>;
  public removeTag!: BelongsToManyRemoveAssociationMixin<Tag, number>;
  public countTags!: BelongsToManyCountAssociationsMixin;
  public createTag!: BelongsToManyCreateAssociationMixin<Tag>;

  public static associations: {
    tags: Association<Image, Tag>;
  };
}

Image.init(
  {
    id: {
      type: DataTypes.UUID,
      defaultValue: Sequelize.UUIDV4,
      primaryKey: true,
    },
    fileName: {
      type: DataTypes.STRING,
    },
    title: {
      type: DataTypes.STRING,
    },
  },
  {
    sequelize,
    modelName: 'image',
  },
);

export default Image;
// index.ts
import instance from './instance';
import Image from './models/Image';
import Tag from './models/Tag';

const ImageTags = Image.belongsToMany(Tag, {
  through: 'ImageTags',
});
const TagImages = Tag.belongsToMany(Image, {
  through: 'ImageTags',
});

instance.sync();

export default instance;
export { Image, ImageTags, Tag, TagImages };
// instance.ts
import { Sequelize } from 'sequelize';
import path from 'path';
import fs from 'fs';

const dbDirectory = path.resolve('data/db');
const storage = path.resolve(dbDirectory, 'gallery.sqlite');

if (!fs.existsSync(dbDirectory)) {
  fs.mkdirSync(dbDirectory);
}
if (!fs.existsSync(storage)) {
  fs.writeFileSync(storage, '');
}

const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage,
});

export default sequelize;

Пример кода, который я пытаюсь использовать:

import { Image as ImageModel, Tag as TagModel } from 'path/to/db';

async function createImage(file) {
  const image = await ImageModel.create<ImageModel>(
   {
      fileName: file.name,
      title: file.name.replace(/\.[a-z]*$/, ''),
    },
  );
  const tag = await TagModel.findOrCreate({ title: 'Untagged' });
  image.addTag(tag); // TypeError: image.addTag is not a function
}

async function getImage(id) {
  const image = await ImageModel.findByPk(id, { include: [ TagModel ] });
  const tags = image.getTags(); // TypeError: image.getTags is not a function
}

Я просмотрел много разных вопросов StackOverflow и другие, которые я мог найти использование as в вызовах belongsToMany, но в моем примере нет.

1 Ответ

0 голосов
/ 06 мая 2020

Работает как положено. Например,

tag.ts:

import { sequelize } from '../../db';
import Sequelize, { Model, DataTypes } from 'sequelize';

class Tag extends Model {
  id!: string;
  title!: string;
}
Tag.init(
  {
    id: {
      type: DataTypes.UUID,
      defaultValue: Sequelize.UUIDV4,
      primaryKey: true,
    },
    title: {
      type: DataTypes.STRING,
      unique: true,
    },
  },
  { sequelize, modelName: 'tag' },
);

export default Tag;

image.ts:

import { sequelize } from '../../db';
import Sequelize, {
  Model,
  DataTypes,
  BelongsToManyGetAssociationsMixin,
  BelongsToManyAddAssociationMixin,
  BelongsToManySetAssociationsMixin,
  BelongsToManyHasAssociationMixin,
  BelongsToManyRemoveAssociationMixin,
  BelongsToManyCountAssociationsMixin,
  BelongsToManyCreateAssociationMixin,
  Association,
} from 'sequelize';
import Tag from './tag';

class Image extends Model {
  id!: string;
  fileName!: string;
  title!: string;
  Tags!: Tag[];

  public getTags!: BelongsToManyGetAssociationsMixin<Tag>;
  public addTag!: BelongsToManyAddAssociationMixin<Tag, number>;
  public setTags!: BelongsToManySetAssociationsMixin<Tag, number>;
  public hasTag!: BelongsToManyHasAssociationMixin<Tag, number>;
  public removeTag!: BelongsToManyRemoveAssociationMixin<Tag, number>;
  public countTags!: BelongsToManyCountAssociationsMixin;
  public createTag!: BelongsToManyCreateAssociationMixin<Tag>;

  public static associations: {
    tags: Association<Image, Tag>;
  };
}
Image.init(
  {
    id: {
      type: DataTypes.UUID,
      defaultValue: Sequelize.UUIDV4,
      primaryKey: true,
    },
    fileName: {
      type: DataTypes.STRING,
    },
    title: {
      type: DataTypes.STRING,
    },
  },
  { sequelize, modelName: 'image' },
);

export default Image;

index.ts:

import Image from './image';
import Tag from './tag';
import { sequelize } from '../../db';

const ImageTags = Image.belongsToMany(Tag, {
  through: 'ImageTags',
});
const TagImages = Tag.belongsToMany(Image, {
  through: 'ImageTags',
});

export default sequelize;
export { Image, ImageTags, Tag, TagImages };

main.ts:

import sequelize, { Image as ImageModel, Tag as TagModel } from './';

async function createImage(file) {
  const image = await ImageModel.create<ImageModel>({
    fileName: file.name,
    title: file.name.replace(/\.[a-z]*$/, ''),
  });
  const [tag, created] = await TagModel.findOrCreate({ where: { title: 'Untagged' } });
  await image.addTag(tag);
  return image.id;
}

async function getImage(id) {
  const image = await ImageModel.findByPk(id, { include: [TagModel] });
  return image.getTags();
}

(async function test() {
  try {
    await sequelize.sync({ force: true });
    const file = { name: 'avatar.jpg' };
    const imageId = await createImage(file);
    const tags = await getImage(imageId);
    console.log('tags:', tags);
  } catch (error) {
    console.log(error);
  } finally {
    sequelize.close();
  }
})();

Результат выполнения:

Executing (default): DROP TABLE IF EXISTS "ImageTags" CASCADE;
Executing (default): DROP TABLE IF EXISTS "tag" CASCADE;
Executing (default): DROP TABLE IF EXISTS "image" CASCADE;
Executing (default): DROP TABLE IF EXISTS "image" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "image" ("id" UUID , "fileName" VARCHAR(255), "title" VARCHAR(255), PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'image' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): DROP TABLE IF EXISTS "tag" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "tag" ("id" UUID , "title" VARCHAR(255) UNIQUE, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'tag' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): DROP TABLE IF EXISTS "ImageTags" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "ImageTags" ("imageId" UUID  REFERENCES "image" ("id") ON DELETE CASCADE ON UPDATE CASCADE, "tagId" UUID  REFERENCES "tag" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("imageId","tagId"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'ImageTags' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): INSERT INTO "image" ("id","fileName","title") VALUES ($1,$2,$3) RETURNING *;
Executing (220d292c-0ca9-4346-a530-ec18f45ec494): START TRANSACTION;
Executing (220d292c-0ca9-4346-a530-ec18f45ec494): SELECT "id", "title" FROM "tag" AS "tag" WHERE "tag"."title" = 'Untagged';
Executing (220d292c-0ca9-4346-a530-ec18f45ec494): CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response "tag", OUT sequelize_caught_exception text) RETURNS RECORD AS $func_ce264c0247834fcfabde8a9190c361c9$ BEGIN INSERT INTO "tag" ("id","title") VALUES ('2f081666-958d-4513-b566-3d791817492c','Untagged') RETURNING * INTO response; EXCEPTION WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL; END $func_ce264c0247834fcfabde8a9190c361c9$ LANGUAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();
Executing (220d292c-0ca9-4346-a530-ec18f45ec494): COMMIT;
Executing (default): SELECT "imageId", "tagId" FROM "ImageTags" AS "ImageTags" WHERE "ImageTags"."imageId" = '1519daf9-42e2-4d39-bc84-d0869107bb72' AND "ImageTags"."tagId" IN ('2f081666-958d-4513-b566-3d791817492c');
Executing (default): INSERT INTO "ImageTags" ("imageId","tagId") VALUES ('1519daf9-42e2-4d39-bc84-d0869107bb72','2f081666-958d-4513-b566-3d791817492c') RETURNING *;
Executing (default): SELECT "image"."id", "image"."fileName", "image"."title", "tags"."id" AS "tags.id", "tags"."title" AS "tags.title", "tags->ImageTags"."imageId" AS "tags.ImageTags.imageId", "tags->ImageTags"."tagId" AS "tags.ImageTags.tagId" FROM "image" AS "image" LEFT OUTER JOIN ( "ImageTags" AS "tags->ImageTags" INNER JOIN "tag" AS "tags" ON "tags"."id" = "tags->ImageTags"."tagId") ON "image"."id" = "tags->ImageTags"."imageId" WHERE "image"."id" = '1519daf9-42e2-4d39-bc84-d0869107bb72';
Executing (default): SELECT "tag"."id", "tag"."title", "ImageTags"."imageId" AS "ImageTags.imageId", "ImageTags"."tagId" AS "ImageTags.tagId" FROM "tag" AS "tag" INNER JOIN "ImageTags" AS "ImageTags" ON "tag"."id" = "ImageTags"."tagId" AND "ImageTags"."imageId" = '1519daf9-42e2-4d39-bc84-d0869107bb72';
tags: [ tag {
    dataValues:
     { id: '2f081666-958d-4513-b566-3d791817492c',
       title: 'Untagged',
       ImageTags: [ImageTags] },
    _previousDataValues:
     { id: '2f081666-958d-4513-b566-3d791817492c',
       title: 'Untagged',
       ImageTags: [ImageTags] },
    _changed: {},
    _modelOptions:
     { timestamps: false,
       validate: {},
       freezeTableName: true,
       underscored: false,
       paranoid: false,
       rejectOnEmpty: false,
       whereCollection: [Object],
       schema: null,
       schemaDelimiter: '',
       defaultScope: {},
       scopes: {},
       indexes: [],
       name: [Object],
       omitNull: false,
       sequelize: [Sequelize],
       hooks: {} },
    _options:
     { isNewRecord: false,
       _schema: null,
       _schemaDelimiter: '',
       include: [Array],
       includeNames: [Array],
       includeMap: [Object],
       includeValidated: true,
       attributes: [Array],
       raw: true },
    isNewRecord: false,
    ImageTags:
     ImageTags {
       dataValues: [Object],
       _previousDataValues: [Object],
       _changed: {},
       _modelOptions: [Object],
       _options: [Object],
       isNewRecord: false } } ]

Проверить базу:

node-sequelize-examples=# select * from "ImageTags";
               imageId                |                tagId
--------------------------------------+--------------------------------------
 1519daf9-42e2-4d39-bc84-d0869107bb72 | 2f081666-958d-4513-b566-3d791817492c
(1 row)

node-sequelize-examples=# select * from tag;
                  id                  |  title
--------------------------------------+----------
 2f081666-958d-4513-b566-3d791817492c | Untagged
(1 row)

node-sequelize-examples=# select * from image;
                  id                  |  fileName  | title
--------------------------------------+------------+--------
 1519daf9-42e2-4d39-bc84-d0869107bb72 | avatar.jpg | avatar
(1 row)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...