Redesign naming Strategies
Issue Creation Checklist
- [x] I understand that my issue will be automatically closed if I don't fill in the requested information
- [x] I have read the contribution guidelines
The issue
The current way of configuring table & column names is too limited and confusing at the same time.
For the table name, we currently have the options freezeTableName to prevent pluralization & snake-casing, and underscored to enable/disable snake-casing.
For column names, only underscored has an effect.
Finally, the final model name must be available in both singular and plural forms, which is the goal of the name option. But the modelName option already exists and expects the singular form.
Sequelize.useInflection can be used to customize the inflection module, but that module should be considered an internal dependency. Exposing it is a bad idea.
Model options for reference: https://sequelize.org/api/v7/interfaces/modeloptions
There is also no way of configuring how association names are pluralized.
Some dialects use UPPER_SNAKE_CASE as their table & column naming convention. There is no option for it in Sequelize at all.
Finally, as pointed out in issue #13896, Sequelize should by default respect the naming conventions of the dialect when generating table & column names.
That's a lot of issues with the current system.
Prior issues on this: #15165 (and many more)
The solution
Remove options underscored, freezeTableName. We also remove Sequelize.useInflection in favor of a new API that can cover all of these use cases. Option namingStrategy.
namingStrategy
Just like the previous options, it can be configured per model through the model options, or for the whole Sequelize instance through Sequelize's options.
Here is a detailed example of that option with all sub-options configured:
new Sequelize({
define: {
namingStrategy: {
casing: {
// configures how table names are generated from Model names
// Supported values: unchanged (use table name as-is), snake-case, upper-snake-case, or a callback function
table: 'snake-case',
// configures how column names are generated from attribute names
// Same supported values as table
column: 'snake-case'
},
// configures whether table names are pluralized
pluralizeTables: true | false,
// configures the pluralizer, for both tables & associations.
pluralizer: callback
},
},
})
casing.table, casing.column, and casing.pluralizer all accept callbacks, which will receive the string to transform, and returns the transformed output.
Users usually use the same casing for column names & table names, so we can also support this short-hand syntax:
new Sequelize({
define: {
namingStrategy: {
casing: 'snake-case',
},
},
})
namingStrategy's default value
More importantly, users shouldn't have to configure this option. We need to provide good defaults.
Dialects use different conventions, so each dialect should provide its default namingStrategy option. Some dialects like postgres will return snake-case, others like snowflake will return upper-snake-case.
I propose to close issue #13896 in favor of this one, as that other issue doesn't support having different default options based on the dialect.
If the user wants to disable snake-casing, they can simply do this to use the same table name & model names:
new Sequelize({
define: {
namingStrategy: {
casing: 'unchanged',
pluralizeTables: false,
},
},
})
That would be the equivalent of today's freezeTableName
Is this feature dialect-specific?
- [x] No. This feature is relevant to Sequelize as a whole.
Would you be willing to resolve this issue by submitting a Pull Request?
- [x] Yes, I have the time and I know how to start.
Indicate your interest in the addition of this feature by adding the 👍 reaction. Comments such as "+1" will be removed.
Sounds good! Do we want to keep supporting underscored and freezeTableName as deprecated options in v7? (Assuming this will land in v7)
If it's not too complicated sure we can keep them for a major or two (don't know when this will land)
Here is another thing that can use the naming strategy API: Index decorators.
The following example is one of the ways you can create a composite index with our port of sequelize-typescript in sequelize
The first parameter of createIndexDecorator must be the decorator name, because it's used in error messages. The function also requires a "name" parameter which defines the name of the composite index itself.
These two names are very likely to be the same, but with different casing conventions (the first one being for JS, the second for SQL).
The naming strategies API makes it possible to generate the second one automatically, but users should be able to configure a different values for indexes, column names, and table names
const MyIndex = createIndexDecorator('MyIndex', {
name: 'my_index',
});
class User extends Model<InferAttributes<User>> {
@Attribute(DataTypes.STRING)
@MyIndex
declare firstName: string;
@Attribute(DataTypes.STRING)
@MyIndex
declare lastName: string;
}
If it's not too complicated sure we can keep them for a major or two (don't know when this will land)
I'm guessing, it is not too hard to add pluralized: boolean option for a couple of major versions, which is lacking now. E.g., for the original class name SomeCategory:
-
SomeCategory(as is) – achievable ({ freezeTableName: true }) -
SomeCategories(only pluralized) – achievable ({ underscored: false }) -
some_categories(pluralized and underscored) – achievable ({ underscored: true }) -
some_category(only underscored) – not achievable 😢
Following this one, we use Oracle DB and it would be nice to have some of this