Testing for not-model/no default id

This commit is contained in:
Tobe O 2019-04-03 05:57:27 -04:00
parent ee7a6d5f04
commit 832601c8c5
28 changed files with 1649 additions and 113 deletions

View file

@ -1,5 +1,6 @@
# 2.0.0-dev.24 # 2.0.0-dev.24
* Fix a bug that caused syntax errors on `ORDER BY`. * Fix a bug that caused syntax errors on `ORDER BY`.
* Add `pattern` to `like` on string builder. `sanitize` is optional.
# 2.0.0-dev.23 # 2.0.0-dev.23
* Add `@ManyToMany` annotation, which builds many-to-many relations. * Add `@ManyToMany` annotation, which builds many-to-many relations.

View file

@ -239,17 +239,23 @@ class StringSqlExpressionBuilder extends SqlExpressionBuilder<String> {
/// Builds a `LIKE` predicate. /// Builds a `LIKE` predicate.
/// ///
/// To prevent injections, the [pattern] is called with a name that /// To prevent injections, an optional [sanitizer] is called with a name that
/// will be escaped by the underlying [QueryExecutor]. /// will be escaped by the underlying [QueryExecutor]. Use this if the [pattern]
/// is not constant, and/or involves user input.
///
/// Otherwise, you can omit [sanitizer].
/// ///
/// Example: /// Example:
/// ```dart /// ```dart
/// carNameBuilder.like('%Mazda%');
/// carNameBuilder.like((name) => 'Mazda %$name%'); /// carNameBuilder.like((name) => 'Mazda %$name%');
/// ``` /// ```
void like(String Function(String) pattern) { void like(String pattern, {String Function(String) sanitize}) {
_raw = 'LIKE \'' + pattern('@$substitution') + '\''; sanitize ??= (s) => pattern;
query.substitutionValues[substitution] = _value; _raw = 'LIKE \'' + sanitize('@$substitution') + '\'';
query.substitutionValues[substitution] = pattern;
_hasValue = true; _hasValue = true;
_value = null;
} }
void isBetween(String lower, String upper) { void isBetween(String lower, String upper) {

View file

@ -21,7 +21,7 @@ class Relationship {
class HasMany extends Relationship { class HasMany extends Relationship {
const HasMany( const HasMany(
{String localKey = 'id', {String localKey,
String foreignKey, String foreignKey,
String foreignTable, String foreignTable,
bool cascadeOnDelete = false}) bool cascadeOnDelete = false})
@ -36,7 +36,7 @@ const HasMany hasMany = const HasMany();
class HasOne extends Relationship { class HasOne extends Relationship {
const HasOne( const HasOne(
{String localKey = 'id', {String localKey,
String foreignKey, String foreignKey,
String foreignTable, String foreignTable,
bool cascadeOnDelete = false}) bool cascadeOnDelete = false})
@ -63,7 +63,7 @@ class ManyToMany extends Relationship {
final Type through; final Type through;
const ManyToMany(this.through, const ManyToMany(this.through,
{String localKey = 'id', {String localKey,
String foreignKey, String foreignKey,
String foreignTable, String foreignTable,
bool cascadeOnDelete = false}) bool cascadeOnDelete = false})

View file

@ -1,3 +1,9 @@
# 2.0.0-dev.7
* Handle `@ManyToMany`.
* Handle cases where the class is not a `Model`.
* Stop assuming things have `id`, etc.
* Resolve a bug where the `indexType` of `@Column` annotations. would not be found.
# 2.0.0-dev.6 # 2.0.0-dev.6
* Fix bug where an extra field would be inserted into joins and botch the result. * Fix bug where an extra field would be inserted into joins and botch the result.
* Narrow analyzer dependency. * Narrow analyzer dependency.

View file

@ -28,6 +28,7 @@ targets:
- test/models/fruit.dart - test/models/fruit.dart
- test/models/has_map.dart - test/models/has_map.dart
- test/models/role.dart - test/models/role.dart
- test/models/unorthodox.dart
$default: $default:
dependencies: dependencies:
- angel_serialize_generator - angel_serialize_generator

View file

@ -19,6 +19,39 @@ import 'readers.dart';
bool isHasRelation(Relationship r) => bool isHasRelation(Relationship r) =>
r.type == RelationshipType.hasOne || r.type == RelationshipType.hasMany; r.type == RelationshipType.hasOne || r.type == RelationshipType.hasMany;
bool isSpecialId(OrmBuildContext ctx, FieldElement field) {
return field is ShimFieldImpl &&
field is! RelationFieldImpl &&
(field.name == 'id' &&
const TypeChecker.fromRuntime(Model)
.isAssignableFromType(ctx.buildContext.clazz.type));
}
FieldElement findPrimaryFieldInList(
OrmBuildContext ctx, Iterable<FieldElement> fields) {
for (var field_ in fields) {
var field = field_ is RelationFieldImpl ? field_.originalField : field_;
var element = field.getter ?? field;
// print(
// 'Searching in ${ctx.buildContext.originalClassName}=>${field?.name} (${field.runtimeType})');
// Check for column annotation...
var columnAnnotation = columnTypeChecker.firstAnnotationOf(element);
if (columnAnnotation != null) {
var column = reviveColumn(new ConstantReader(columnAnnotation));
// print(
// ' * Found column on ${field.name} with indexType = ${column.indexType}');
if (column.indexType == IndexType.primaryKey) return field;
}
}
var specialId =
fields.firstWhere((f) => isSpecialId(ctx, f), orElse: () => null);
// print(
// 'Special ID on ${ctx.buildContext.originalClassName} => ${specialId?.name}');
return specialId;
}
final Map<String, OrmBuildContext> _cache = {}; final Map<String, OrmBuildContext> _cache = {};
Future<OrmBuildContext> buildOrmContext( Future<OrmBuildContext> buildOrmContext(
@ -59,19 +92,18 @@ Future<OrmBuildContext> buildOrmContext(
for (var field in buildCtx.fields) { for (var field in buildCtx.fields) {
// Check for column annotation... // Check for column annotation...
Column column; Column column;
var columnAnnotation = columnTypeChecker.firstAnnotationOf(field); var element = field.getter ?? field;
var columnAnnotation = columnTypeChecker.firstAnnotationOf(element);
if (columnAnnotation != null) { if (columnAnnotation != null) {
column = reviveColumn(new ConstantReader(columnAnnotation)); column = reviveColumn(new ConstantReader(columnAnnotation));
} }
if (column == null && if (column == null && isSpecialId(ctx, field)) {
field.name == 'id' &&
const TypeChecker.fromRuntime(Model)
.isAssignableFromType(buildCtx.clazz.type)) {
// This is only for PostgreSQL, so implementations without a `serial` type // This is only for PostgreSQL, so implementations without a `serial` type
// must handle it accordingly, of course. // must handle it accordingly, of course.
column = const Column(type: ColumnType.serial); column = const Column(
type: ColumnType.serial, indexType: IndexType.primaryKey);
} }
if (column == null) { if (column == null) {
@ -168,12 +200,30 @@ Future<OrmBuildContext> buildOrmContext(
// Fill in missing keys // Fill in missing keys
var rcc = new ReCase(field.name); var rcc = new ReCase(field.name);
String keyName(OrmBuildContext ctx, String missing) {
var _keyName =
findPrimaryFieldInList(ctx, ctx.buildContext.fields)?.name;
// print(
// 'Keyname for ${buildCtx.originalClassName}.${field.name} maybe = $_keyName??');
if (_keyName == null) {
throw '${ctx.buildContext.originalClassName} has no defined primary key, '
'so the relation on field ${buildCtx.originalClassName}.${field.name} must define a $missing.';
} else {
return _keyName;
}
}
if (type == RelationshipType.hasOne || type == RelationshipType.hasMany) { if (type == RelationshipType.hasOne || type == RelationshipType.hasMany) {
localKey ??= 'id'; localKey ??=
foreignKey ??= '${rc.snakeCase}_id'; ctx.buildContext.resolveFieldName(keyName(ctx, 'local key'));
// print(
// 'Local key on ${buildCtx.originalClassName}.${field.name} defaulted to $localKey');
foreignKey ??= '${rc.snakeCase}_$localKey';
} else if (type == RelationshipType.belongsTo) { } else if (type == RelationshipType.belongsTo) {
localKey ??= '${rcc.snakeCase}_id'; foreignKey ??=
foreignKey ??= 'id'; ctx.buildContext.resolveFieldName(keyName(foreign, 'foreign key'));
localKey ??= '${rcc.snakeCase}_$foreignKey';
} }
var relation = new RelationshipReader( var relation = new RelationshipReader(
@ -187,19 +237,21 @@ Future<OrmBuildContext> buildOrmContext(
throughContext: throughContext, throughContext: throughContext,
); );
// print('Relation on ${buildCtx.originalClassName}.${field.name} => '
// 'foreignKey=$foreignKey, localKey=$localKey');
if (relation.type == RelationshipType.belongsTo) { if (relation.type == RelationshipType.belongsTo) {
var name = new ReCase(relation.localKey).camelCase; var name = new ReCase(relation.localKey).camelCase;
ctx.buildContext.aliases[name] = relation.localKey; ctx.buildContext.aliases[name] = relation.localKey;
if (!ctx.effectiveFields.any((f) => f.name == field.name)) { if (!ctx.effectiveFields.any((f) => f.name == field.name)) {
// TODO: Consequences of allowing ID to be a relation? (should be none) var foreignField = relation.findForeignField(ctx);
// if (field.name != 'id' || var foreign = relation.throughContext ?? relation.foreign;
// !const TypeChecker.fromRuntime(Model) var type = foreignField.type;
// .isAssignableFromType(ctx.buildContext.clazz.type)) { if (isSpecialId(foreign, foreignField))
var rf = new RelationFieldImpl(name, type = field.type.element.context.typeProvider.intType;
field.type.element.context.typeProvider.intType, field.name); var rf = new RelationFieldImpl(name, relation, type, field);
ctx.effectiveFields.add(rf); ctx.effectiveFields.add(rf);
// }
} }
} }
@ -239,17 +291,16 @@ ColumnType inferColumnType(DartType type) {
} }
Column reviveColumn(ConstantReader cr) { Column reviveColumn(ConstantReader cr) {
var args = cr.revive().namedArguments;
IndexType indexType = IndexType.none;
ColumnType columnType; ColumnType columnType;
if (args.containsKey('index')) { var columnObj =
indexType = cr.peek('type')?.objectValue?.getField('name')?.toStringValue();
IndexType.values[args['indexType'].getField('index').toIntValue()]; var indexType = IndexType.values[
} cr.peek('indexType')?.objectValue?.getField('index')?.toIntValue() ??
IndexType.none.index];
if (args.containsKey('type')) { if (columnObj != null) {
columnType = new _ColumnType(args['type'].getField('name').toStringValue()); columnType = new _ColumnType(columnObj);
} }
return new Column( return new Column(
@ -283,9 +334,15 @@ class _ColumnType implements ColumnType {
} }
class RelationFieldImpl extends ShimFieldImpl { class RelationFieldImpl extends ShimFieldImpl {
final String originalFieldName; final FieldElement originalField;
RelationFieldImpl(String name, DartType type, this.originalFieldName) final RelationshipReader relationship;
RelationFieldImpl(
String name, this.relationship, DartType type, this.originalField)
: super(name, type); : super(name, type);
String get originalFieldName => originalField.name;
PropertyAccessorElement get getter => originalField.getter;
} }
InterfaceType firstModelAncestor(DartType type) { InterfaceType firstModelAncestor(DartType type) {

View file

@ -8,7 +8,6 @@ import 'package:angel_serialize_generator/build_context.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart' hide LibraryBuilder; import 'package:code_builder/code_builder.dart' hide LibraryBuilder;
import 'package:source_gen/source_gen.dart'; import 'package:source_gen/source_gen.dart';
import 'orm_build_context.dart'; import 'orm_build_context.dart';
var floatTypes = [ var floatTypes = [
@ -434,21 +433,8 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var queryInstance = queryType.newInstance([]); var queryInstance = queryType.newInstance([]);
// Next, we need to apply a cascade that sets the correct query value. // Next, we need to apply a cascade that sets the correct query value.
var localField = ctx.effectiveFields.firstWhere( var localField = relation.findLocalField(ctx);
(f) => var foreignField = relation.findForeignField(ctx);
ctx.buildContext.resolveFieldName(f.name) ==
relation.localKey, orElse: () {
throw '${ctx.buildContext.clazz.name} has no field that maps to the name "${relation.localKey}", '
'but it has a @HasMany() relation that expects such a field.';
});
var foreignField = foreign.effectiveFields.firstWhere(
(f) =>
foreign.buildContext.resolveFieldName(f.name) ==
relation.foreignKey, orElse: () {
throw '${foreign.buildContext.clazz.name} has no field that maps to the name "${relation.foreignKey}", '
'but ${ctx.buildContext.clazz.name} has a @HasMany() relation that expects such a field.';
});
var queryValue = (isSpecialId(ctx, localField)) var queryValue = (isSpecialId(ctx, localField))
? 'int.parse(model.id)' ? 'int.parse(model.id)'
@ -505,10 +491,17 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var merged = merge.join(', '); var merged = merge.join(', ');
var keyName =
findPrimaryFieldInList(ctx, ctx.buildContext.fields)?.name;
if (keyName == null) {
throw '${ctx.buildContext.originalClassName} has no defined primary key.\n'
'@HasMany and @ManyToMany relations require a primary key to be defined on the model.';
}
b.body = new Code(''' b.body = new Code('''
return super.$methodName(executor).then((result) { return super.$methodName(executor).then((result) {
return result.fold<List<$type>>([], (out, model) { return result.fold<List<$type>>([], (out, model) {
var idx = out.indexWhere((m) => m.id == model.id); var idx = out.indexWhere((m) => m.$keyName == model.$keyName);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model);
@ -525,14 +518,6 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}); });
} }
bool isSpecialId(OrmBuildContext ctx, FieldElement field) {
return field is ShimFieldImpl &&
field is! RelationFieldImpl &&
(field.name == 'id' &&
const TypeChecker.fromRuntime(Model)
.isAssignableFromType(ctx.buildContext.clazz.type));
}
Class buildWhereClass(OrmBuildContext ctx) { Class buildWhereClass(OrmBuildContext ctx) {
return new Class((clazz) { return new Class((clazz) {
var rc = ctx.buildContext.modelClassNameRecase; var rc = ctx.buildContext.modelClassNameRecase;
@ -667,13 +652,11 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}); });
})); }));
// Each field generates a getter for setter // Each field generates a getter and setter
for (var field in ctx.effectiveFields) { for (var field in ctx.effectiveFields) {
var fType = field.type; var fType = field.type;
var name = ctx.buildContext.resolveFieldName(field.name); var name = ctx.buildContext.resolveFieldName(field.name);
var type = isSpecialId(ctx, field) var type = convertTypeReference(field.type);
? refer('int')
: convertTypeReference(field.type);
clazz.methods.add(new Method((b) { clazz.methods.add(new Method((b) {
var value = refer('values').index(literalString(name)); var value = refer('values').index(literalString(name));
@ -748,9 +731,15 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// Add only if present // Add only if present
var target = refer('values').index(literalString( var target = refer('values').index(literalString(
ctx.buildContext.resolveFieldName(field.name))); ctx.buildContext.resolveFieldName(field.name)));
var parsedId = (refer('int') var foreign = field.relationship.throughContext ??
.property('parse') field.relationship.foreign;
.call([prop.property('id')])); var foreignField = field.relationship.findForeignField(ctx);
var parsedId = prop.property(foreignField.name);
if (isSpecialId(foreign, field)) {
parsedId = (refer('int').property('parse').call([parsedId]));
}
var cond = prop.notEqualTo(literalNull); var cond = prop.notEqualTo(literalNull);
var condStr = cond.accept(new DartEmitter()); var condStr = cond.accept(new DartEmitter());
var blkStr = var blkStr =

View file

@ -1,4 +1,5 @@
import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type.dart';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel_orm/angel_orm.dart';
import 'package:source_gen/source_gen.dart'; import 'package:source_gen/source_gen.dart';
@ -43,4 +44,23 @@ class RelationshipReader {
bool get isManyToMany => bool get isManyToMany =>
type == RelationshipType.hasMany && throughContext != null; type == RelationshipType.hasMany && throughContext != null;
FieldElement findLocalField(OrmBuildContext ctx) {
return ctx.effectiveFields.firstWhere(
(f) => ctx.buildContext.resolveFieldName(f.name) == localKey,
orElse: () {
throw '${ctx.buildContext.clazz.name} has no field that maps to the name "$localKey", '
'but it has a @HasMany() relation that expects such a field.';
});
}
FieldElement findForeignField(OrmBuildContext ctx) {
var foreign = throughContext ?? this.foreign;
return foreign.effectiveFields.firstWhere(
(f) => foreign.buildContext.resolveFieldName(f.name) == foreignKey,
orElse: () {
throw '${foreign.buildContext.clazz.name} has no field that maps to the name "$foreignKey", '
'but ${ctx.buildContext.clazz.name} has a @HasMany() relation that expects such a field.';
});
}
} }

View file

@ -88,7 +88,7 @@ main() {
}); });
test('union', () async { test('union', () async {
var query1 = new BookQuery()..where.name.like((_) => 'Deathly%'); var query1 = new BookQuery()..where.name.like('Deathly%');
var query2 = new BookQuery()..where.authorId.equals(-1); var query2 = new BookQuery()..where.authorId.equals(-1);
var query3 = new BookQuery() var query3 = new BookQuery()
..where.name.isIn(['Goblet of Fire', 'Order of the Phoenix']); ..where.name.isIn(['Goblet of Fire', 'Order of the Phoenix']);

View file

@ -0,0 +1,89 @@
import 'package:test/test.dart';
import 'models/unorthodox.dart';
import 'common.dart';
main() {
PostgresExecutor executor;
setUp(() async {
executor =
await connectToPostgres(['unorthodox', 'weird_join', 'song', 'numba']);
});
test('can create object with no id', () async {
var query = UnorthodoxQuery()..values.name = 'Hey';
var model = await query.insert(executor);
expect(model, Unorthodox(name: 'Hey'));
});
group('relations on non-model', () {
Unorthodox unorthodox;
setUp(() async {
var query = UnorthodoxQuery()..values.name = 'Hey';
unorthodox = await query.insert(executor);
});
test('belongs to', () async {
var query = WeirdJoinQuery()..values.joinName = unorthodox.name;
var model = await query.insert(executor);
print(model.toJson());
expect(model.id, isNotNull); // Postgres should set this.
expect(model.unorthodox, unorthodox);
});
group('layered', () {
WeirdJoin weirdJoin;
Song girlBlue;
setUp(() async {
var wjQuery = WeirdJoinQuery()..values.joinName = unorthodox.name;
weirdJoin = await wjQuery.insert(executor);
var gbQuery = SongQuery()
..values.weirdJoinId = weirdJoin.id
..values.title = 'Girl Blue';
girlBlue = await gbQuery.insert(executor);
});
test('has one', () async {
var query = WeirdJoinQuery()..where.id.equals(weirdJoin.id);
var wj = await query.getOne(executor);
print(wj.toJson());
expect(wj.song, girlBlue);
});
test('has many', () async {
var numbas = <Numba>[];
for (int i = 0; i < 15; i++) {
var query = NumbaQuery()
..values.parent = weirdJoin.id
..values.i = i;
var model = await query.insert(executor);
numbas.add(model);
}
var query = WeirdJoinQuery()..where.id.equals(weirdJoin.id);
var wj = await query.getOne(executor);
print(wj.toJson());
expect(wj.numbas, numbas);
});
test('many to many', () async {
var fooQuery = FooQuery()..values.bar = 'baz';
var fooBar = await fooQuery.insert(executor).then((foo) => foo.bar);
var pivotQuery = FooPivotQuery()
..values.weirdJoinId = weirdJoin.id
..values.fooBar = fooBar;
await pivotQuery.insert(executor);
fooQuery = FooQuery()..where.bar.equals('baz');
var foo = await fooQuery.getOne(executor);
print(foo.toJson());
print(weirdJoin.toJson());
expect(foo.weirdJoins[0].id, weirdJoin.id);
});
});
});
}

View file

@ -0,0 +1,7 @@
CREATE TEMPORARY TABLE "numbas" (
"i" int,
"parent" int references weird_joins(id),
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY(i)
);

View file

@ -0,0 +1,8 @@
CREATE TEMPORARY TABLE "songs" (
"id" serial,
"weird_join_id" int references weird_joins(id),
"title" varchar(255),
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY(id)
);

View file

@ -0,0 +1,4 @@
CREATE TEMPORARY TABLE "unorthodoxes" (
"name" varchar(255),
PRIMARY KEY(name)
);

View file

@ -0,0 +1,13 @@
CREATE TEMPORARY TABLE "weird_joins" (
"id" serial,
"join_name" varchar(255) references unorthodoxes(name),
PRIMARY KEY(id)
);
CREATE TEMPORARY TABLE "foos" (
"bar" varchar(255),
PRIMARY KEY(bar)
);
CREATE TEMPORARY TABLE "foo_pivots" (
"weird_join_id" int references weird_joins(id),
"foo_bar" varchar(255) references foos(bar)
);

View file

@ -11,7 +11,9 @@ class AuthorMigration extends Migration {
up(Schema schema) { up(Schema schema) {
schema.create('authors', (table) { schema.create('authors', (table) {
table.serial('id')..primaryKey(); table.serial('id')..primaryKey();
table.varChar('name')..defaultsTo('Tobe Osakwe'); table.varChar('name', length: 255)
..defaultsTo('Tobe Osakwe')
..unique();
table.timeStamp('created_at'); table.timeStamp('created_at');
table.timeStamp('updated_at'); table.timeStamp('updated_at');
}); });
@ -107,11 +109,11 @@ class AuthorQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
String get name { String get name {
return (values['name'] as String); return (values['name'] as String);
} }

View file

@ -137,11 +137,11 @@ class BookQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
int get authorId { int get authorId {
return (values['author_id'] as int); return (values['author_id'] as int);
} }
@ -172,10 +172,10 @@ class BookQueryValues extends MapQueryValues {
createdAt = model.createdAt; createdAt = model.createdAt;
updatedAt = model.updatedAt; updatedAt = model.updatedAt;
if (model.author != null) { if (model.author != null) {
values['author_id'] = int.parse(model.author.id); values['author_id'] = model.author.id;
} }
if (model.partnerAuthor != null) { if (model.partnerAuthor != null) {
values['partner_author_id'] = int.parse(model.partnerAuthor.id); values['partner_author_id'] = model.partnerAuthor.id;
} }
} }
} }

View file

@ -138,11 +138,11 @@ class CarQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
String get make { String get make {
return (values['make'] as String); return (values['make'] as String);
} }

View file

@ -102,11 +102,11 @@ class CustomerQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
DateTime get createdAt { DateTime get createdAt {
return (values['created_at'] as DateTime); return (values['created_at'] as DateTime);
} }

View file

@ -112,11 +112,11 @@ class FootQueryValues extends MapQueryValues {
return {'n_toes': 'decimal'}; return {'n_toes': 'decimal'};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
int get legId { int get legId {
return (values['leg_id'] as int); return (values['leg_id'] as int);
} }

View file

@ -112,11 +112,11 @@ class FruitQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
int get treeId { int get treeId {
return (values['tree_id'] as int); return (values['tree_id'] as int);
} }

View file

@ -107,11 +107,11 @@ class HasCarQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
CarType get type { CarType get type {
return CarType.values[(values['type'] as int)]; return CarType.values[(values['type'] as int)];
} }

View file

@ -119,11 +119,11 @@ class LegQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
String get name { String get name {
return (values['name'] as String); return (values['name'] as String);
} }

View file

@ -144,11 +144,11 @@ class OrderQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
int get customerId { int get customerId {
return (values['customer_id'] as int); return (values['customer_id'] as int);
} }
@ -186,7 +186,7 @@ class OrderQueryValues extends MapQueryValues {
createdAt = model.createdAt; createdAt = model.createdAt;
updatedAt = model.updatedAt; updatedAt = model.updatedAt;
if (model.customer != null) { if (model.customer != null) {
values['customer_id'] = int.parse(model.customer.id); values['customer_id'] = model.customer.id;
} }
} }
} }

View file

@ -11,7 +11,7 @@ class TreeMigration extends Migration {
up(Schema schema) { up(Schema schema) {
schema.create('trees', (table) { schema.create('trees', (table) {
table.serial('id')..primaryKey(); table.serial('id')..primaryKey();
table.declare('rings', ColumnType('smallint')); table.integer('rings');
table.timeStamp('created_at'); table.timeStamp('created_at');
table.timeStamp('updated_at'); table.timeStamp('updated_at');
}); });
@ -179,11 +179,11 @@ class TreeQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
int get rings { int get rings {
return (values['rings'] as int); return (values['rings'] as int);
} }

View file

@ -0,0 +1,70 @@
import 'package:angel_migration/angel_migration.dart';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize/angel_serialize.dart';
part 'unorthodox.g.dart';
@serializable
@orm
abstract class _Unorthodox {
@Column(indexType: IndexType.primaryKey)
String get name;
}
@serializable
@orm
abstract class _WeirdJoin {
@primaryKey
int get id;
@BelongsTo(localKey: 'join_name', foreignKey: 'name')
_Unorthodox get unorthodox;
@hasOne
_Song get song;
@HasMany(foreignKey: 'parent')
List<_Numba> get numbas;
@ManyToMany(_FooPivot)
List<_Foo> get foos;
}
@serializable
@orm
abstract class _Song extends Model {
int get weirdJoinId;
String get title;
}
@serializable
@orm
class _Numba implements Comparable<_Numba> {
@primaryKey
int i;
int parent;
int compareTo(_Numba other) => i.compareTo(other.i);
}
@serializable
@orm
abstract class _Foo {
@primaryKey
String get bar;
@ManyToMany(_FooPivot)
List<_WeirdJoin> get weirdJoins;
}
@serializable
@orm
abstract class _FooPivot {
@belongsTo
_WeirdJoin get weirdJoin;
@belongsTo
_Foo get foo;
}

File diff suppressed because it is too large Load diff

View file

@ -228,11 +228,11 @@ class UserQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
String get username { String get username {
return (values['username'] as String); return (values['username'] as String);
} }
@ -368,10 +368,10 @@ class RoleUserQueryValues extends MapQueryValues {
set userId(int value) => values['user_id'] = value; set userId(int value) => values['user_id'] = value;
void copyFrom(RoleUser model) { void copyFrom(RoleUser model) {
if (model.role != null) { if (model.role != null) {
values['role_id'] = int.parse(model.role.id); values['role_id'] = model.role.id;
} }
if (model.user != null) { if (model.user != null) {
values['user_id'] = int.parse(model.user.id); values['user_id'] = model.user.id;
} }
} }
} }
@ -535,11 +535,11 @@ class RoleQueryValues extends MapQueryValues {
return {}; return {};
} }
int get id { String get id {
return (values['id'] as int); return (values['id'] as String);
} }
set id(int value) => values['id'] = value; set id(String value) => values['id'] = value;
String get name { String get name {
return (values['name'] as String); return (values['name'] as String);
} }

View file

@ -78,7 +78,7 @@ main() {
}); });
test('union', () async { test('union', () async {
var query1 = new CarQuery()..where.make.like((_) => '%Fer%'); var query1 = new CarQuery()..where.make.like('%Fer%');
var query2 = new CarQuery()..where.familyFriendly.isTrue; var query2 = new CarQuery()..where.familyFriendly.isTrue;
var query3 = new CarQuery()..where.description.equals('Submarine'); var query3 = new CarQuery()..where.description.equals('Submarine');
var union = query1.union(query2).unionAll(query3); var union = query1.union(query2).unionAll(query3);
@ -89,7 +89,7 @@ main() {
test('or clause', () async { test('or clause', () async {
var query = new CarQuery() var query = new CarQuery()
..where.make.like((_) => 'Fer%') ..where.make.like('Fer%')
..orWhere((where) => where ..orWhere((where) => where
..familyFriendly.isTrue ..familyFriendly.isTrue
..make.equals('Honda')); ..make.equals('Honda'));