Saving a single model in sqflite is quite easy. I am trying to save the nested Model in sqfilte. Model class, table creation query, and error is explained below. Any help will be appreciated to save such nested models:
Main Model:
#JsonSerializable(explicitToJson: true)
class Album {
String? name;
int? playcount;
String? url;
ArtistDetail? artist;
List<Image>? image;
Album({this.name, this.playcount, this.url, this.artist, this.image});
factory Album.fromJson(Map<String, dynamic> json) =>
_$AlbumFromJson(json);
Map<String, dynamic> toJson() => _$AlbumToJson(this);
}
Sub-model:
#JsonSerializable()
class Image {
dynamic _text;
String? _size;
dynamic get text => _text;
String? get size => _size;
Image({dynamic text, String? size}) {
_text = text;
_size = size;
}
factory Image.fromJson(Map<String, dynamic> json) => _$ImageFromJson(json);
Map<String, dynamic> toJson() => _$ImageToJson(this);
}
Function to store image into sqflite:
// Table creation query
//'CREATE TABLE $TABLE ( $ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, $ALBUM_NAME //TEXT, $PLAY_COUNT INTEGER, $ALBUM_URL TEXT, $ARTIST_DETAIL TEXT, $IMAGES TEXT )'
//Function to perform insertion:
Future<int> insert(Album album) async {
var dbClient = await db;
return await dbClient.insert(
TABLE,
album.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Error:
Invalid argument [{#text: https:5d72a77281bec2f7ddea87c48.png, size: small}] with type List<Map<String, dynamic>>. The only num, String, and Uint8List are supported.
Indeed nested List or Map are not supported in SQLite, Only simple types are supported (String, num and Uint8List) at the "root" level. You have to "flatten" your model somehow.
For inner objects, You could decide for example to save the Artist details as a JSON text or put each artist field in the Album object.
For list, you could as well encode the list as JSON or using a proprietary format.
You can find here an example of solutions for a nested object.
For example, the following simple map is not supported:
{
"title": "Table",
"size": {"width": 80, "height": 80}
}
It should be flattened. One solution is to modify the map structure:
CREATE TABLE Product (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
width INTEGER,
height INTEGER)
{"title": "Table", "width": 80, "height": 80}
Another solution is to encoded nested maps and lists as json (or other format), declaring the column
as a String.
CREATE TABLE Product (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
size TEXT
)
{
'title': 'Table',
'size': '{"width":80,"height":80}'
};
Related
I'm using Prisma 3.7.0 and Postgresql 13. I need to create a many-to-many relation between two models that has some additional data, but I don't know how to do this in a Prisma schema. The fields I want to add are not appropriately added to either table.
Here's some simplified (contrived) SQL to illustrate the goal:
It's pretty straight-forward in SQL, but I don't quite understand from the Prisma docs how to do it.
CREATE TABLE animal (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL -- e.g. "tiger" or "armadillo"
)
CREATE TABLE animal_bodyparts (
animal INT,
bodypart INT,
-- I want to add these two fields to the relation.
count INT, -- How many of this bodypart the animal has
is_vital BOOLEAN, -- Does the creature die if this part is damaged?
PRIMARY KEY (animal, bodypart)
CONSTRAINT fk_animal FOREIGN KEY (animal) REFERENCES animal(id),
CONSTRAINT fk_bodypart FOREIGN KEY (bodypart) REFERENCES bodypart(id)
)
CREATE TABLE bodypart (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL -- e.g. "head" or "leg"
)
And here's the best I could figure out with Prisma so far:
model Animal {
id Int #id #default(autoincrement)
name String // e.g. "tiger" or "armadillo"
parts BodyPart[]
}
model BodyPart {
id Int #id #default(autoincrement)
name String // e.g. "head" or "leg"
animals Animal[]
}
But where do I put the count and is_vital columns in the Prisma models? I see in a one-to-many relation there's some way to add some info through the #relation tag, but I don't think it addresses this need.
You can do it as follow:
datasource mysql {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
generator erd {
provider = "prisma-erd-generator"
output = "entity-relationship-diagram.svg"
}
model Animal {
id Int #id #default(autoincrement())
name String
animalAndBodyParts AnimalAndBodyParts[] #relation()
}
model AnimalAndBodyParts {
id Int #id #default(autoincrement())
count Int
isVital Boolean #map("is_vital")
animal Animal #relation(fields: [animalId], references: [id])
animalId Int
bodyPart BodyPart #relation(fields: [bodyPartId], references: [id])
bodyPartId Int
}
model BodyPart {
id Int #id #default(autoincrement())
name String
animalAndBodyParts AnimalAndBodyParts[] #relation()
}
It will generate a database as show the image:
You can save data in this way:
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
const saveData = async () => {
const animal = await prisma.animal.create({
data: {
name: 'Lion',
animalAndBodyParts: {
create: {
count: 2,
isVital: true,
bodyPart: {
create: {
name: 'Eye',
},
},
},
},
},
include: {
animalAndBodyParts: {
include: {
bodyPart: true,
},
},
},
})
console.log(JSON.stringify(animal, null, 2));
}
saveData()
And the data will look like in the image down below
I want to create an empty array in Firestore while posing a feed. But the array is showing null in Firestore. Here is my code. Please help.
class FeedModel {
final String imgUrl;
final String desc;
final String authorName;
final String profileImg;
final String title;
final int likeCount;
List<String> strArr = []; // this the array i want to create it in firestore
FeedModel({this.imgUrl, this.desc,this.authorName,this.profileImg,this.title,this.likeCount, this.strArr});
Map<String, dynamic> toMap(){
return {
"imgUrl" : this.imgUrl,
"desc" : this.desc,
"authorName" : this.authorName,
"profileImg" : this.profileImg,
"like_count" : this.likeCount,
"liked_user_id" : this.strArr
};
}
}
Here is the send data code:
Future<void> _sendData() async {
try {
final StorageReference firebaseStorageRef = FirebaseStorage.instance.ref().child('myimage.jpg');
final StorageUploadTask task = firebaseStorageRef.putFile(_image);
StorageTaskSnapshot taskSnapshot = await task.onComplete;
String downloadUrl = await taskSnapshot.ref.getDownloadURL();
final String pname = myController.text;
final String pimgurl = downloadUrl;
final String pauthorName = "Sachin Tendulkar";
final String pprofileImg = "https://i.picsum.photos/id/564/200/200.jpg?hmac=uExb18W9rplmCwAJ9SS5NVsLaurpaCTCBuHZdhsW25I";
final String ptitle = "Demo Data";
final int plikeCount= 0;
List<String> pLikeduserId; // This line returning null as show in image
print(pimgurl);
final FeedModel feeds = FeedModel(imgUrl: pimgurl ,desc: pname,authorName: pauthorName ,profileImg: pprofileImg,title: ptitle,likeCount: plikeCount, strArr : pLikeduserId );
insertData(feeds.toMap());
} catch (e) {
print(e);
}
}
null image:
want to get like below image:
How can i send array like last image when creating a feed?
If you want an array in a field, even an empty array, you will have to assign it an actual list value. Right now, by not assigning any actual list value at all, you're effectively assigning a null value to the liked_user_id.
So, just give it a value:
List<String> pLikeduserId = [];
That will write an empty list field.
There isn't much of a reason to store it as an array with a value of ''. Instead, it would be better to null check when receiving the value from firebase by using something like: liked_userid = /*insert method of getting firebase user ID*/??[]
This way when you use the liked_userid it won't be null and will be an empty list.
If you really want a list with the value of '' inside than change the insides of the toMap function:
Map<String, dynamic> toMap(){
return {
"imgUrl" : this.imgUrl,
"desc" : this.desc,
"authorName" : this.authorName,
"profileImg" : this.profileImg,
"like_count" : this.likeCount,
"liked_user_id" : this.strArr=[]?['']:this.strArr//line that was changed
};
}
This would make it so that you will have an array with [''] inside but I would still recommend the first method I showed.
Do this to create an array with an empty string.
Map<String, dynamic> toMap() {
return {
'liked_user_id': List.generate(1, (r) => "")
};
}
await _fireStore.collection("COLLECTION_NAME").document("DOUCUMENT_NAME").setData({
"KEY_VALUE": [],
});
This creates an empty Array in Cloud FireStore.
Consider the following classes
class Category {
var tag: String?
var itemList: [Item]?
}
class Item {
var id: Int?
var type: String?
var itemDetails: ItemDetails?
}
class ItemDetails {
var description: String?
var name: String?
var price: Float?
}
Given an array of Category objects.
var categoryList: [Category]
I want to create a new object array by extracting only the name in ItemDetails(inorder to apply a filter) and an id inorder to reconstruct back array of Category objects.
Hence, I have to reconstruct the array of Category objects
from new object array.
How to do both extraction and reconstruction using the map feature?
Below are the examples of other data sources:
Datasource 1 :
var categoryList: [Category], where name need to be extracted
Datasource 2 :
var searchList = [SearchItem], where title to be extracted.
Class SearchItem {
var id: Int?
var details: SearchItemDetails?
var type: String?
}
Class SearchItemDetails {
var description: String?
var title: String?
}
DataSource 3
var products: [Products], where title to be extracted.
Class Products {
var id: Int?
var details: ProductDetails?
var type: String?
}
class ProductDetails {
var description: String?
var title: String?
}
To get an array of just the names, you do the map like you mentioned:
let categories: [Category] = ... // this already exists
let itemNames = categories.map { $0.itemList?.map({ $0.itemDetails?.name }) }
But this will preserve optionals. If you don't want optionals, then use compactMap instead.
Reconstructing, however, doesn't really make any sense. Are you using the ID to hit a database or network service? That's the only possible way you'd be able to reconstruct the original array of Categorys. Why not just hold onto the original array?
I'm trying to send a Map<String, dynamic>, and one of the dynamic is actually a List<Map<String, dynamic>>.
I assemble it like that :
Packet packet = Packet(
datas: stocks.map((stock) => stock.toJson()).toList(),
);
String json = jsonEncode(packet);
The problem is what's being sent is actually this :
{
"datas": {
"rows": "[{NoArticle: 051131626638, Description: Ruban pour tapis, qty: 5}]"
}
}
The expected output is this :
{
"datas": {
"rows": [{
"NoArticle": "051131626638",
"Description": "Ruban pour tapis",
"qty": 5,
}]
}
}
I want to send a List<Map<String, dynamic>>, not a String. How do I do that?
Answer : I am a dumbass.
I passed the parameter trough a function, like this :
Server.send("sendInventoryBatch", {
"rows": "${stocks.map((stock) => stock.toJson()).toList()}",
});
Of course it would return a String.
Question is invalid. If you have a similar problem, please open a different question. Sorry for the inconvenience.
Now if anyone actually has this question and stumbles upon this thread, here's how to do it :
Assemble your object, and make sure you didn't stringify it along the way
Use jsonEncode
In case of doubt, make everything a Map<String, dynamic>, a List<dynamic>, or a sub-class of those two first.
Packet packet = Packet(
appType: "inventoryManager",
module: "",
action: action,
datas: data,
deviceID: Globals.map["UUID"],
cbackid: cback,
);
You can generate classes like my custom Packet from JSON using multiple online resources because jsonEncode will use the auto-generated Map<String, dynamic> toJson().
https://app.quicktype.io/ - Was recommended to me on Discord by Miyoyo#5957
https://javiercbk.github.io/json_to_dart/ - I used this one before
http://json2dart.com/
String json = jsonEncode(packet);
And voilà, you're done.
Did you look at packages json_serializable?
Here's an example of a Person that has many Orders : example
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:json_annotation/json_annotation.dart';
part 'example.g.dart';
#JsonSerializable()
class Person {
final String firstName;
#JsonKey(includeIfNull: false)
final String middleName;
final String lastName;
#JsonKey(name: 'date-of-birth', nullable: false)
final DateTime dateOfBirth;
#JsonKey(name: 'last-order')
final DateTime lastOrder;
#JsonKey(nullable: false)
List<Order> orders;
Person(this.firstName, this.lastName, this.dateOfBirth,
{this.middleName, this.lastOrder, List<Order> orders})
: orders = orders ?? <Order>[];
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
#JsonSerializable(includeIfNull: false)
class Order {
int count;
int itemNumber;
bool isRushed;
Item item;
#JsonKey(
name: 'prep-time',
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration prepTime;
#JsonKey(fromJson: _dateTimeFromEpochUs, toJson: _dateTimeToEpochUs)
final DateTime date;
Order(this.date);
factory Order.fromJson(Map<String, dynamic> json) => _$OrderFromJson(json);
Map<String, dynamic> toJson() => _$OrderToJson(this);
static Duration _durationFromMilliseconds(int milliseconds) =>
Duration(milliseconds: milliseconds);
static int _durationToMilliseconds(Duration duration) =>
duration.inMilliseconds;
static DateTime _dateTimeFromEpochUs(int us) =>
DateTime.fromMicrosecondsSinceEpoch(us);
static int _dateTimeToEpochUs(DateTime dateTime) =>
dateTime.microsecondsSinceEpoch;
}
#JsonSerializable()
class Item {
int count;
int itemNumber;
bool isRushed;
Item();
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
Map<String, dynamic> toJson() => _$ItemToJson(this);
}
#JsonLiteral('data.json')
Map get glossaryData => _$glossaryDataJsonLiteral;
This question already has an answer here:
Why is an _id with ObjectID added to when using MongoDB's $push to add new object to an array?
(1 answer)
Closed 9 years ago.
I have the following Mongoose schema for a node js app I'm working on:
var memory = new schema({
date_added: Date,
name: String,
description: String,
personsInvolved: [{person: String}],
location: String,
uniqueness: Number,
category: String
});
It has an array field called personsInvolved that just has one field in it, person.
In my app, there's a form that takes a list of people, separated by a comma and it goes to a create function that takes that value and splits it on the commas into an array.
That part works, but it adds an _id field to each person in the array when I save the document into mongo. It looks like this:
personsInvolved:
[ { person: 'Test', _id: 52c6d6c2457e5ce02b00000b },
{ person: ' test2', _id: 52c6d6c2457e5ce02b00000c },
{ person: ' test3', _id: 52c6d6c2457e5ce02b00000d } ] }
Is there a way to make it so the _id field doesn't get added to each person? This is the code I'm using to save the record to mongo:
exports.create = function(req, res) {
// people are separated by a comma
var people = req.body.people;
var peopleArr = req.body.people.split(",");
var newMem = new memory();
newMem.date_added = Date.now();
newMem.name = req.body.name;
newMem.description = req.body.description;
for(var i in peopleArr) {
var name = {person: peopleArr[i]};
newMem.personsInvolved.push(name);
}
newMem.location = req.body.location;
newMem.uniqueness = req.body.uniqueness;
newMem.category = req.body.category;
console.log(newMem);
newMem.save(function(err, memory, count) {
res.redirect('/');
});
};
I'm only using personsInvolved as data, the persons in the array are not being used to identify with any other documents.
Make person a real schema model (as opposed to an anonymous object) and pass it {_id:false}.
http://mongoosejs.com/docs/subdocs.html