Как в Sequelize (TypeScript ) unique:false для связи BelongsToMany (Many-to-Many)

Рейтинг: 0Ответов: 1Опубликовано: 05.04.2023

В проекте используется nestJS, PgSQL, orm Sequelize. Есть 3 таблицы: services и visitors, которые связаны между собой (@BelongsToMany), через таблицу visitors_services. Проблема в том, что в базу данных все время запмывается visitors_services_visitor_id_service_id_key, этот uniq мешает работе, как это убрать в коде? Чтобы данной записи не происходило, а связь осталась.

Лишняя запись в БД

Модель visitor (visitor.model.ts)

import { ApiProperty } from "@nestjs/swagger/dist/decorators";
import { BelongsToMany, Column, DataType, ForeignKey, HasMany, Model, Table, Unique } from "sequelize-typescript";
import { Service } from "src/services/services.model";
import { VisitorService } from "src/visitors_services/visitors_services.model";

interface VisitorCreationAttrs {
    /*
        ...
    */
}

@Table({ tableName: 'visitors' })
export class Visitor extends Model<Visitor, VisitorCreationAttrs>{
    @ApiProperty({ example: 1, description: 'PK unic indicate' })
    @Column({ type: DataType.INTEGER, unique: true, autoIncrement: true, primaryKey: true })
    id: number;
    /*
        ...
    */
    @BelongsToMany(() => Service, () => VisitorService)
    Services: Service[];
}

Модель services (service.model.ts)

import { ApiProperty } from "@nestjs/swagger/dist/decorators/api-property.decorator";
import { BelongsToMany, Column, DataType, Model, Table } from "sequelize-typescript";
import { Visitor } from "src/visitors/visitor.model";
import { VisitorService } from "src/visitors_services/visitors_services.model";

interface ServiceCreationAttrs {
    /*
        ...
    */
}

@Table({ tableName: 'services' })
export class Service extends Model<Service, ServiceCreationAttrs>{

    @ApiProperty({ example: 1, description: 'PK unic indicate' })
    @Column({ type: DataType.INTEGER, unique: true, autoIncrement: true, primaryKey: true })
    id: number;
    /*
        ...
    */
    @BelongsToMany(() => Visitor, () => VisitorService)
    Visitors: Visitor[];
}

Модель visitors_services (visitor-service.model.ts)

import { ApiProperty } from "@nestjs/swagger/dist/decorators";
import { Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript";
import { Service } from "src/services/services.model";
import { Visitor } from "src/visitors/visitor.model";

interface VisitorServiceCreationAttrs {
    visitor_id: number;
    service_id: number;
}

@Table({ tableName: 'visitors_services' })
export class VisitorService extends Model<VisitorService, VisitorServiceCreationAttrs>{
    @ApiProperty({ example: 1, description: 'PK unic indicate' })
    @Column({ type: DataType.INTEGER, unique: true, autoIncrement: true, primaryKey: true })
    id: number;

    @ApiProperty({ example: 1, description: 'Ссылка на id посетителя' })
    @ForeignKey(() => Visitor)
    @Column({ type: DataType.INTEGER, allowNull: false, unique: false })
    visitor_id: number;

    @ApiProperty({ example: 1, description: 'Ссылка на id услуги' })
    @ForeignKey(() => Service)
    @Column({ type: DataType.INTEGER, allowNull: false, unique: false })
    service_id: number;
}

Ответы

▲ 0

Я пересмотрел данный способ. Видимо в "BelongsToMany" разработчиками было заложено то, что связь уникальна (связь {visitorId: 1, roleId: 1} может встретиться только один раз, при том что может быть {visitorId: 1, roleId: 2} ... {visitorId: 1, roleId: N} ). По этому я решил сделать отношения между своими моделями, как представленно здесь https://sebhastian.com/sequelize-nested-include/.

Данный способ меня устроил. Пока не выявил недостатков для себя.

Изменения в коде:

  • В моделях Visitor, Service были удалены строки с @BelongsToMan
  • В модель Visitor были добавленны строки:
export class Visitor extends Model<Visitor, VisitorCreationAttrs>{
    ...
    @HasMany(() => VisitorServices)
    visitorServices: VisitorServices[]
}
  • В модель VisitorService были добавленны строки:
export class VisitorServices extends Model<VisitorServices, VisitorServiceCreationAttrs>{    
    ...    
    @BelongsTo(() => Service)
    service: Service[]
}

Получается что Visitor имеет много VisitorServices, а VisitorServices пренадлежит Services. Данные отношения моделей помогают мне достать данные из бд в виде:

[
    {
        "name": "VisitorName",
        ...// VisitorData
        "visitorServices": [
            {
                "id": 1, // visitorServiceId
                ...// visitorServicesData
                "service": {
                  ...// ServiceData
                }
            },
            {
                "id": 3,
                ...
                "service": {
                ...
                }
            }
        ]
    },
    {
        "name": "Visitor2Name",
        ...
        "visitorServices": [
            {
                "id": 2,
                ...
                "service": {
                ...
                }
            }
        ]
    }
]

Если вдруг кому потребуется, оставлю тут этот кусок кода для получения данных в виде как описано выше (надеюсь ничего не упустил, если что подправьте):

import { Injectable, Inject } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { Visitor } from './visitor.model';
import { Service } from 'src/services/services.model';
import { VisitorServices } from 'src/visitors_services/visitors_services.model';

export class VisitorsService {

constructor(@InjectModel(Visitor) private visitorRepository: typeof Visitor){ }

async getVisitorsServices(visitorsId: number[]) {
        const visitorsServices = await this.visitorRepository.findAll({
            where: { id: visitorsId },
            include: [
                {
                    model: VisitorServices,
                    attributes: ['id'],
                    required: true,
                    include: [
                        {
                            model: Service,
                            required: true,
                            attributes: ['price', 'title'],
                        }
                    ]
                }],
            attributes: ['name']
        });
        return visitorsServices
    }
}

;)