How to prevent Child models from Deletion in Golang GORM? - go

Well, I would like to know, Is there any solutions, how to prevent Child Model from deletion in foreignKey Constraint, (
For example in gorm there is a couple of options that allows to restrict behavior of the Parent Model after deletion, and Delete or Set to Null the Child Model Objects That has foreignKey relation (onDelete: Cascade / Set Null, and the same thing for onUpdate)
// Pretty a lot of the same words, but I hope you got it :)
Little Example.. from Golang ...
type SomeOtherStruct struct {
gorm.Model
Id int
}
type SomeModel struct {
gorm.Model
someOtherStructId string
someField SomeOtherStruct `gorm:"foreignKey:SomeOtherStructId; OnDelete:Cascade,OnUpdate: SET NULL"` // basically foreign Key Relationship to model `SomeOtherStruct`
}
But I would like to prevent any Update/Deletion behavior, so Child Relation Models Objects won't get deleted after the Parent Model Object has been..
There is actually a concept from Django Framework (Python)
class SomeModel(models.Model):
some_field = models.ForeignKey(to=AnotherModel, verbose_name="SomeField", on_delete=models.PROTECT)
class AnotherModel(models.Model):
pass
As you can see, there is models.PROTECT constraint, that is basically what I'm looking for....
Is there any analogy for that in Golang GORM or some RAW SQL for that as well?
Thanks..

Unfortunately, you didn't mention which database you are using.
In Postgres (as an example) there are multiple options for ON DELETE:
NO ACTION
RESTRICT
CASCADE
SET NULL
SET DEFAULT
Only CASCADE will delete children if the parent is deleted. All other options (including the default NO ACTION) will make sure the children will "survive".
You can find more information in the postgres documentation: https://www.postgresql.org/docs/current/sql-createtable.html
Please feel free to update your question and/or comment with the database you are using.

Related

Go gorm creating many to many relationship using customised table and extra data

I am trying to achieve a join between two models using gorm and a customised table. On this customised table I would like to store extra information about the joined models.
As per the docs I have the following,
func init() {
database.DB.SetupJoinTable(&Foo{}, "Bar", &FooBar{})
}
type Foo struct {
UUID uuid.UUID `json:"uuid" gorm:"type:uuid;primaryKey;"`
SomeValue string `json:"someValue"`
Bars []Bar `json:"bars" gorm:"many2many:foo_bars"`
}
type Bar struct {
UUID uuid.UUID `json:"uuid" gorm:"type:uuid;primaryKey;"`
SomeValue string `json:"someValue"`
}
type FooBar struct {
FooUUID uuid.UUID `json:"foo" gorm:"primaryKey"`
BarUUID uuid.UUID `json:"bar" gorm:"primaryKey"`
ExtraValue string `json:"extraValue"`
}
The above creates a schema as I would expect. The problem is when trying to persist this relationship and set extra data needed to FooBar. The docs provide documentation on appending relationships directly from Foo to Bar, but not adding data to this customised table.
The documentation states:
JoinTable can be a full-featured model
So I would expect this to be possible.
Is this possible using gorm?
How can I create and save this relationship with additional information?
This is actually very simple, the fact that FooBar is a full-featured model means you can use it to interact with the join table directly the same way that any other model can:
// assuming we have a Foo foo and Bar bar
// read
links := []FooBar{}
db.Where(FooBar{FooUUID: foo.UUID, BarUUID: bar.UUID}).Find(&links)
// create
link := FooBar{FooUUID: foo.UUID, BarUUID: bar.UUID, ExtraValue: "foobar"}
db.Create(&link)
// update
link.ExtraValue = "foobarbaz"
db.Save(&link)
// delete
db.Delete(&link)
But to do all this requires you to treat the join table as a separate model. For example you shouldn't expect to be able to extract the ExtraValue when doing regular Associations operations on Foo, because there is literally no space on the []Bar field to put the value. Similar goes for saving the ExtraValue, unless you do some special trickery with Hooks (see this answer for more on this).

How do I query an optional column with a secondary index using phantom?

I have a secondary index on an optional column:
class Sessions extends CassandraTable[ConcreteSessions, Session] {
object matchId extends LongColumn(this) with PartitionKey[Long]
object userId extends OptionalLongColumn(this) with Index[Option[Long]]
...
}
However, the indexedToQueryColumn implicit conversion is not available for optional columns, so this does not compile:
def getByUserId(userId: Long): Future[Seq[Session]] = {
select.where(_.userId eqs userId).fetch()
}
Neither does this:
select.where(_.userId eqs Some(userId)).fetch()
Or changing the type of the index:
object userId extends OptionalLongColumn(this) with Index[Long]
Is there a way to perform such a query using phantom?
I know that I could denormalize, but it would involve some very messy housekeeping and triple our (substantial) data size. The query usually returns only a handful of results, so I'd be willing to use a secondary index in this case.
Short answer: You could not use optional fields in order to query things in phantom.
Long detailed answer:
But, if you really want to work with secondary optional columns, you should declare your entity field as Option but your phantom representation should not be an option in order to query.
object userId extends LongColumn(this) with Index[Long]
In the fromRow(r: Row) you can create your object like this:
Sessions(matchId(r), Some(userId(r)))
Then in the service part you could do the following:
.value(_.userId, t.userId.getOrElse(0))
You also have a better way to do that. You could duplicate the table, making a new kind of query like sessions_by_user_id where in this table your user_id would be the primary key and the match_id the clustering key.
Since user_id is optional, you would end with a table that contains only valid user ids, which is easy and fast to lookup.
Cassandra relies on queries, so use it in your favor.
Take a look up on my github project that helps you get up with multiple queries in the same table.
https://github.com/iamthiago/cassandra-phantom

How do I setup super- and sub-type relationships in CodeIgnitor's DataMapper ORM?

I'm running an online food journal where users can record journal entries. There are four types of entries: food entries, exercise entries, measurements, and completed tasks. Entries have several attributes in common (e.g., id, amount, unit_id, etc), but they also have type-specific attributes (e.g., food_id, exercise_id, etc). That's a perfect candidate for a super- and sub-type relationship.
Here are my tables (simplified):
CREATE TABLE entries
`id` int
`user_id` int
`created` datetime
`entered` datetime
`amount` float
`unit_id` int
PRIMARY KEY id
CREATE TABLE exercise_entries
`entry_id` int
`exercise_id` int
PRIMARY KEY entry_id
CREATE TABLE food_entries
`entry_id` int
`food_id` int
PRIMARY KEY entry_id
So my question is, how do I setup super- and sub-type relationships using CodeIgniter's DataMapper ORM? I've looked at the User Guide's Relationships and Advanced Relationships sections, but I can't find anything.
If it's not possible with DataMapper, I can think of a few solutions:
Roll sub-type attributes up (1 big table, ugh)
Roll super-type attributes down (4 separate tables, ugh)
Go nuclear and use Doctrine 2.0 ORM (YAML config files, ugh!)
Use a different framework whose native ORM supports table inheritance (I shortlisted Kohana and FuelPHP with CodeIgniter).
Manually code super- and sub-type relationships (defeats purpose of ORM in the first place).
I'm not thrilled with any of my options. Option 1 and 2 create their own headaches (see the bottom of this article). Option 3 seems like surgery with a sledgehammer. I'm open to Option 4 because I haven't started writing any framework code (it was a really tough choice between CI and Kohana). Option 5 is where I am now.
Any suggestions? Thanks for the help!
I haven't tried this with DataMapper, but you might try (making sure to call the parent constructor and all that). I would assume that Exerciseentry would inherit all of the properties/methods from Entry - but not sure if DataMapper would handle it this way:
class Entry extends DataMapper {
}
// you may have to explicitly include Entry.php in this file:
class Exerciseentry extends Entry {
}
If that doesn't work, you can basically create two objects that are related ( not really pure OOP principle, but would get the job done ):
class Entry extends DataMapper {
// ... some stuff
var $has_many = array('exerciseentry', 'foodentry');
// ... some more stuff
}
class Exerciseentry extends DataMapper {
// ... some stuff
var $has_one = array('entry');
// ... some more stuff
}
class Foodentry extends DataMapper {
// ... some stuff
var $has_one = array('entry');
// ... some more stuff
}
// then when you get an entry, you'd do this
$my_exercise_entry = new Exerciseentry(1);
$my_exercise_entry->include_related('entry', array('user_id', 'amount', 'unit_id');
$my_exercise_entry->get();
echo 'Amount is: ' . $my_exercise_entry->entry_amount;
// etc

Table with a foreign key

how can I build a table of "orders" containing "IdOrder", "Description" and "User"?... the "User" field is a reference to the table "Users", which has "IdUser" and "Name". I'm using repositories.
I have this repository:
Repository<Orders> ordersRepo = new OrderRepo<Orders>(unitOfWork.Session);
to return all Orders to View, I just do:
return View(ordersRepo.All());
But this will result in something like:
IdOrder:1 -- Description: SomeTest -- User: UserProxy123ih12i3123ih12i3uh123
-
When the expected result was:
IdOrder:1 -- Description: SomeTest -- User: Thiago.
PS: I don't know why it returns this "UserProxy123ih12i3123ih12i3uh123". In Db there is a valid value.
The View:
It is showed in a foreach (var item in Model).
#item.Description
#item.User //--> If it is #item.User.Name doesn't work.
What I have to do to put the Name on this list? May I have to do a query using LINQ - NHibernate?
Tks.
What type of ORM are you using? You mention "repositories" but does that mean LinqToSql, Entity Framework, NHibernate, or other?
It looks like you are getting an error because the User field is not loaded as part of the original query. This is likely done to reduce the size of the result set by excluding the related fields from the original query for Orders.
There are a couple of options to work around this:
Set up the repository (or context, depending on the ORM) to include the User property in the result set.
Explicitly load the User property before you access it. Note that this would be an additional round-trip to the database and should not be done in a loop.
In cases where you know that you need the User information it would make sense to ensure that this data in returned from the original query. If you are using LinqToSql take a look at the DataLoadOptions type. You can use this type to specify which relationships you want to retrieve with the query:
var options = new DataLoadOptions();
options.LoadWith<Orders>(o => o.User);
DataContext context = ...;
context.LoadOptions = options;
var query = from o in context.Orders
select o;
There should be similar methods to achive the same thing whatever ORM you are using.
In NHibernate you can do the following:
using (ISession session = SessionFactory.OpenSession())
{
var orders = session.Get<Order>(someId);
NHibernateUtil.Initialize(orders.User);
}
This will result in only two database trips (regardless of the number of orders returned). More information on this can be found here.
In asp.net MVC the foreign key doesn't work the way you are using it. I believe you have to set the user to a variable like this:
User user = #item.User;
Or you have to load the reference sometimes. I don't know why this is but in my experience if I put this line before doing something with a foreign key it works
#item.UserReference.load();
Maybe when you access item.User.Name the session is already closed so NHib cannot load appropriate user from the DB.
You can create some model and initialize it with proper values at the controller. Also you can disable lazy loading for Orders.User in your mapping.
But maybe it is an other problem. What do you have when accessing "#item.User.Name" from your View?

best practice Treeview populating from differents kinds of objects

I would like to populate a Treeview.
Here is what I have in DB :
table : Box
BoxID
BoxName
table Book :
BookID
BookName
BoxID (fk Box.BoxID)
table Chapter:
ChapterID
ChapterName
BookID (fk Book.BookID)
As you may know a treeview is made up of treenode object.
A treenode object have a text property and a tag property.
The "text" property is the text that it's display on the screen for this node and the "tag" is an "hidden" value (usually uses to identify a node)
So in my case; the fields ending with ID will be used in the "tag" property and the fields ending with Name will be used in the "text" property
example :
so for a book; I will use the BookID field for the "tag" property and BookName field for the "text" property
note : I use a dbml so I have a Book object, Box object and Chapter object and I use linq to get them from the db.
So my question is; what is the best practice to build this tree?
I have a solution but it's really ugly because it looks like I'm duplicating the code.
The problem is that the values I need to extract for the text and tag properties are identified by differents fields name in the db
So for the book level, I need to get the BookID field to populate the tag property of my node; for the box level, I need to get the BoxID field to populate the tag property , ....
How can I make a kind of generic way to do it ?
I hope I made myself clear enough, don't hesitate to ask me questions :)
Thx in advance
Here is what I have for the moment
I get the list of box with a linq (dbml) request.
List<Box> MyListofBox = getMyListofBox();
Treenode tnBox = null;
Treenode tnBook =null;
foreach(Box b in MyListofBox )
{
tnBox = new TreeNode();
tnBox.tag = b.BoxID;
tnBox.text = b.BoxName;
List<Book> MyListofBook = getMyListofBookByBoxID(b.BoxID)
foreach(Book boo in MyListofBook )
{
tnBook = new TreeNode();
tnBook.tag = boo.BookID;
tnBook.text = boo.BookName;
tnBox.nodes.add(tnBook);
}
mytreeview.nodes.add(tnBox);
}
but of course I don't like this solution...
do you have a better way ?
I would extract the you need from the database in the form of a struct, possibly via the anonnoumous type that has been added to C# together with linq. Then I would populate insert this data into the place in the tree.
From what I get, you are trying to get each property separately, which will not work so well, because then you will have to make a call to the database for each separate property, which is very wasteful.
Addition based on what you have added
I do not believe the code can be more compact - the names you call are similar, but not the same and the way you do it was what I was trying to explain earlier.
You could
Define an key/value interface that both Box and Book implement
Define a delegate that returns a TreeNode and create delegate methods that accept Box and Book
However, I think the code is fine as written. Sometimes you just have to code it and there's little point in further abstracting or optimizing it.
The only issue I see in the code is that you're making a database call in a loop. Whether or not that's a problem depends on the application.

Resources