[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reuse collections of fields inside models #2371

Open
richardwardza opened this issue May 5, 2020 · 30 comments
Open

Reuse collections of fields inside models #2371

richardwardza opened this issue May 5, 2020 · 30 comments
Labels
domain/psl Issue in the "PSL" domain: Prisma Schema Language domain/schema Issue in the "Schema" domain: Prisma Schema, Introspection, Migrations etc. kind/feature A request for a new feature. topic: mixins topic: modular-schema topic: schema

Comments

@richardwardza
Copy link
richardwardza commented May 5, 2020

Problem

I have a number of models in my schema and most of them include the following 4 fields:

  createdAt     DateTime   @default(now())
  createdBy     String
  updatedAt     DateTime   @updatedAt
  updatedBy     String

It would be great if this could be created as a collection in the file and then just that used in the models.
It could also be used to ensure standards are maintained across the various models (like the StandardId snippet below)

Solution

A solution would be to allow collections in the schema file. Possibly something like:

collection AuditRecords {
  createdAt     DateTime   @default(now())
  createdBy     String
  updatedAt     DateTime   @updatedAt
  updatedBy     String
}

collection StandardId {
  id          String   @default(cuid()) @id
}

Model User {
  @collection['StandardId']
  name String
  email String
  @collection['AuditRecords']
}

Model SomethingElse {
  id         Int       @id @default(autoincrement())
  name String
  email String
  @collection['AuditRecords']
}
@pantharshit00
Copy link
Contributor

Hi,

Thanks for reporting this! It has already been reported before, so I am going to close this as a duplicate of #1291

@janpio
Copy link
Member
janpio commented May 6, 2020

#1291 is about importing models. This request is about being able to reuse collections of fields inside models. That is quite different and I think justifies a separate issue @pantharshit00.

@richardwardza Updating the title of this issue might help to make the feature more precise 👍

@janpio janpio reopened this May 6, 2020
@pantharshit00
Copy link
Contributor

@janpio I was referring to extending part of that FR:

import "dependency/prisma/core.prisma"

extend model Post {
  title String @pg.varchar(50)
  slug String
}

This is essentially the same thing as this feature request but with a different syntax.

@richardwardza richardwardza changed the title Allow snippets in the schema file Reuse collections of fields inside models May 6, 2020
@richardwardza
Copy link
Author

Thanks @janpio. I've updated the title - collection is a nicer name too!

@thebiglabasky
Copy link

Looks like inspiration could be taken from mixins in the Sass world
Interesting for sure. We should look at that as part of a larger research on enabling multipart schemas which can be composed.

@pantharshit00
Copy link
Contributor

Alternate proposal from #3520 (comment)

abstract model Basics {
  id        Int       @id @default(autoincrement())
  updatedAt DateTime  @updatedAt
  createdAt DateTime  @default(now())
}

model User extends Basics {
  email String @unique
}

model Task extends Basics {
  title String
}

@thebiglabasky
Copy link

Yes, that follows the inheritance pattern over a composition one.
That's a trade-off we'll have to make: how close to a programming pattern does the schema stay, etc. But good to keep that for more context.

@reflash
Copy link
reflash commented Oct 6, 2020

Alternate proposal from #3520 (comment)

abstract model Basics {
  id        Int       @id @default(autoincrement())
  updatedAt DateTime  @updatedAt
  createdAt DateTime  @default(now())
}

model User extends Basics {
  email String @unique
}

model Task extends Basics {
  title String
}

I think this approach is fine either if there's a way to extend from multiple models.
In my current scenario I need something similar to what is described in the original post as in some models I'll need only one mixin and in some - multiple

@Schubidu
Copy link
Schubidu commented Oct 13, 2020

In GQL there exists an interface may that's more the direction to implement inheritance here?

interface Basics {
  updatedAt DateTime  @updatedAt
  createdAt DateTime  @default(now())
}

interface StandardID {
  id        Int       @id @default(autoincrement())
}

model User implements Basics, StandardID {
  email String @unique
}

model Task implements Basics, StandardID {
  title String
}

@johannesschobel
Copy link
Contributor

I would highly favour some kind of extends solution that is close to programming paradigms..

@MWhite-22
Copy link
MWhite-22 commented Feb 24, 2021

Any input on this from the prisma team? Im working with a lot of tables that all have the same general meta data columns, and other share type specific columns. Typing out every new model as a whole is tedious and I big detractor in value from things like TypeORM which allow Entity inheritance, extension, and embedding.

I am a fan of the interface/implements and abstract/extends approach above (both appear very similar assuming the extends allows extending multiple abstract models), but I really like the value in the OP for things like embedded objects. For example:

collection Base {
  id            String @id @default(cuid())
  createdAt     DateTime   @default(now())
  createdBy     String
}

collection Address {
  street1      String
  street2      String?
  city         String
  state        String
  zip          String
}

model Building{
  //...other fields
  @collection('Base')
  @collection('Address', embed) 
  //@collection( CollectionName: string, InheritType: 'spread' | 'embed' = 'spread')
}

Would result in tables field structure and interface below:

TABLE Building {
  //... other columns
  id uuid PRIMARY KEY NOT NULL;
  createdAt timestamptz NOT NULL;
  createdBy uuid NOT NULL;
  address_street1 varchar NOT NULL;
  address_street2 varchar NULL; 
  address_city varchar NOT NULL; 
  address_state varchar NOT NULL; 
  address_zip varchar NOT NULL; 
}
interface Building {
  //... other columns
  id: string;
  createdAt: Date;
  createdBy: string; //Expanded to user if relationship created in model
  address: {
    street1: string;
    street2?: string;
    city: string;
    state: string;
    zip: string;
  }
}

I assume the interface/inherit or abstract/extends style would auto spread all inherited fields directly on to the model. The @collection decorator could allow for some very powerful customization and code reusability.

@pantharshit00 pantharshit00 added the domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. label Apr 23, 2021
@chinanderm
Copy link

This would be awesome to have. Is there a way the community can vote on prioritized features? Wouldn't want this to get lost in the noise.

@janpio
Copy link
Member
janpio commented Oct 7, 2021

Leave a 👍 reaction on the original issue - we use that to inform our internal prioritization.

@janpio janpio added the domain/psl Issue in the "PSL" domain: Prisma Schema Language label Jan 7, 2022
@janpio janpio added the domain/schema Issue in the "Schema" domain: Prisma Schema, Introspection, Migrations etc. label Feb 6, 2022
@jonschlinkert
Copy link
jonschlinkert commented Jun 20, 2022

An alternative approach:

fragment Basics {
  updatedAt DateTime  @updatedAt
  createdAt DateTime  @default(now())
}

model User {
  id String @id @default(cuid())
  include(Basics)
}

model Task {
  id String @id @default(cuid())
  include(Basics)
}

If I could only choose one thing, "fragments" or "inheritance", I'd much prefer fragments. The reason is that fragments are simply "expanding" the schema during a preprocessing step, whereas "inheritance" (via extends) implies that something much more complicate is going on with the database (even if it's not). Also, inheriting a model happens at the top-level, and is all-or-nothing, whereas multiple includes/partials/fragments can be injected into a model wherever you want them to be. I choose the syntax in my example since it's easy to parse and disambiguate that syntax from other existing features, and it will allow the syntax to play nicely with fields that were defined inline.

FWIW, IMHO the hard part is making a decision on the syntax. Once that decision is made, the implementation should be pretty simple, and could be implemented without having to re-write the current parser since we're just "expanding" the schema. I'd be happy to do a PR, or write some code that accomplishes what I suggested (or what someone else suggested) if you'd consider adding this as a pre-processing step.

Which brings up one more possibility: you could add support for the preprocessor keyword, which is similar to generator but would obviously run first (before parsing), and would be expected to return a valid Prisma schema.

@jonschlinkert
Copy link

FWIW, I considered implementing something like this either as a VS Code extension or as a custom script in our build step. But I'd probably need to fork the syntax highlighter too so we could edit the schema without fragment causing errors, which would be a lot of work

In the meantime as you consider this, I created this pre-processor after posting my last comment, as a proof of concept: https://gist.github.com/jonschlinkert/55a3f68ad5eb77c4a0b77e24a275f04a

IMHO, validation should not be done in this step, since the parser already does validation. It would make more sense to simply "expand" the schema, and then run the parser and provide feedback at that stage.

@kerimcharfi
Copy link
kerimcharfi commented Jun 24, 2022

I made a simple CLI tool called prisma-compositor that merges multiple prisma schema files from an input directory and allows you to use fragments.

fragment TimeStamps {
    updatedAt DateTime  @updatedAt
    createdAt DateTime  @default(now())
}

model User {
    id String @id @default(cuid())
    ...TimeStamps
}

Installation
npm i prisma-compositor --save-dev

Usage
npx prisma-compositor ./schemas compiled-schema.prisma

@jonschlinkert
Copy link

@kerimcharfi I like that spread syntax a lot! Better than the syntax I suggested.

@matzeso
Copy link
matzeso commented Nov 4, 2022

Would love to have something like this. For me also composition > inheritance.
I like the suggested name fragment, or maybe trait. 👍 +1 on the spread syntax, for me a block level annotation like @@includes(FragmentName) would also work.
In the end I wouldn't mind much the naming as long as such a feature makes it into the roadmap :)

@janpio janpio removed the domain/client Issue in the "Client" domain: Prisma Client, Prisma Studio etc. label Nov 12, 2022
@EverStarck
Copy link

Any update of this? 😞

@ortonomy
Copy link

Why is this fundamental feature not in this ORM?

@janpio
Copy link
Member
janpio commented Dec 20, 2022

Short answer: Because we are busy building other fundamental features that can not be worked around by copy pasting a few fields around. As soon as we have capacity, we will take a look at this and design and implement a solution.

@SwapnilSoni1999

This comment was marked as off-topic.

@afonsomatos

This comment was marked as off-topic.

@Chris-Sahyouni
Copy link

There's also a tool called zenstack that allows abstract/extends model inheritance. The only thing is the abstract models basically just have their fields copied over into their child models so they never get their own dedicated typescript types. It would be great to see this integrated into prisma but with an option to generate dedicated types for the base models. Also, it would be super useful to see the inheritance structure reflected on the typescript side, but I understand this is most likely not possible because the generated types are just types and not interfaces or classes.

@kindywu
Copy link
kindywu commented Dec 24, 2023

Love zenstack, but I think a fragment implementation would be cooler

@schutzie
Copy link

To take this one step further... I have a similar use case where each of my models contains a createdById and a modifiedById. Each should automatically be populated by a user ID from the application. So in addition to just including the fields as part of the model, I'd love to be able to use some kind of global variable or function in my application (Remix, but same concept for any stack) to auto-populate that value - something like a custom function, but also part of the EXTENDS approach. Each "create" and "update" function automatically grabs a value via logic that I specify in some kind of central location - a function, etc.

@johannesschobel
Copy link
Contributor

Dear all,

again, i would like to throw in prismerge (see here: https://github.com/prisma-utils/prisma-utils/blob/main/libs/prismerge/README.md ) which will address this issue.

All the best

@OrionWambert
Copy link

Any News ?

@tinezmatias
Copy link

+1

@domvanrob
Copy link

Dear all,

again, i would like to throw in prismerge (see here: https://github.com/prisma-utils/prisma-utils/blob/main/libs/prismerge/README.md ) which will address this issue.

All the best

The docs are already outdated :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
domain/psl Issue in the "PSL" domain: Prisma Schema Language domain/schema Issue in the "Schema" domain: Prisma Schema, Introspection, Migrations etc. kind/feature A request for a new feature. topic: mixins topic: modular-schema topic: schema
Projects
None yet
Development

No branches or pull requests