How to associate two models in Laravel 9? - laravel-9

I have two models, Book and Author. Book has a belongsTo relationship to Author. How can I add an author to a book?
$book = new Book;
$author = new Author;
Tried this:
$book->author($author);
And this:
$book->author = $author;
Both don't work.

$book->author()->associate($author);

Related

Field bindings in peewee

How I can link fields in peewee as in sqlalchemy:
parent_id = Column(Integer, ForeignKey("parent.id"))
How to convert this code to peewee?
The very first document in the documentation describes creating a foreign key relationship. Instead of taking the time to write your question and post it here, you could have read that doc. I'm linking it here: http://docs.peewee-orm.com/en/latest/peewee/quickstart.html#model-definition
Example models:
from peewee import *
db = SqliteDatabase('people.db')
class Person(Model):
name = CharField()
birthday = DateField()
class Meta:
database = db # This model uses the "people.db" database.
class Pet(Model):
owner = ForeignKeyField(Person, backref='pets')
name = CharField()
animal_type = CharField()
class Meta:
database = db # this model uses the "people.db" database
For a self-referential foreign key: http://docs.peewee-orm.com/en/latest/peewee/models.html#self-referential-foreign-keys
Example:
class Category(Model):
name = CharField()
parent = ForeignKeyField('self', null=True, backref='children')

How can I create a Schema in Marshmallow to reverse-nest queried data?

Sorry if this sounds silly, but i'm trying to get all the books for an author. This is what I have:
class Author(db.Model):
__tablename__='author'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Book(db.Model):
__tablename__='book'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
author_id = db.Column(db.Integer, ForeignKey('author.id'))
author_rel = relationship('Author', lazy='joined'), backref='book')
and I have my schemas:
class BookSchema(Schema):
id = fields.Int()
name= field.Str()
class BookSchema(Schema):
id = fields.Int()
title = field.Str()
author = fields.Nested(Author)
So I can retrieve the books with the author and the authors.
What I need here is to add a nested field with all the books of each author... I've been trying, but failing to do so. Is there an automatic way to get this?
I'm trying to join the tables in the query, but also failing to do it:
session.query(Author, Book).join(Book, Author.id == Book.author_id).all()
This gives me a (Author, Book) tuple, and I cannot map that into a concise json... How could I do that?
EDIT:
Ok, so apparently I didn't understand the concept of a relationship haha I could avoid all this trouble by simply adding a relationship to my Author entity:
class Author(db.Model):
__tablename__='author'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
books = relationship('Book', lazy='joined', backref='author_book')
class Book(db.Model):
__tablename__='book'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
author_id = db.Column(db.Integer, ForeignKey('author.id'))
author = relationship('Author', lazy='joined', backref='book_author')
Then I could just populate my marshmallow schema normally and be happy
==================================================
The solution I used was:
session = Session()
author_objects = session.query(Author).all()
schema = AuthorSchema(many=True)
author_list = schema.dump(author_objects).data
book_objects = session.query(Book).all()
schema = BookSchema(many=True)
book_list = schema.dump(book_objects).data
for index, author in enumerate(author_list):
author_list[index]['books'] = [book for book in book_list if book['author_id'] == author['id']]
session.close()
return jsonify(author_list)
This feels kinda manual for me, I think there should be a better way to do this automatically using schemas. It's, after all, based on a relationship that exists.
This works, but would be slow for long lists... I preferred to do this using sql-alchemy + marshmallow directly...
Ideas?

Django Model Design (Big model vs multiple)

I have a question about designing my models. Suppose I have a following model:
class Comment(models.Model):
who = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=1000)
likes = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='child_comments')
Now I would like models for multiple topics (ShoppingList, Games,...). I came to two possible solutions and need help with deciding more suitable one.
1) Make Comment abstract and extend it for every new model wanted.
class ShoppingListComment(Comment):
shopping_list = models.ForeignKey(ShoppingList, related_name='shopping_comments', on_delete=models.CASCADE)
I could then query this game comments with something like:
ShoppingListComment.objects.all()
2) Add extra nullable Foreing keys directly to comment:
class BigCommentModel(models.Model):
who = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=1000)
likes = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='child_comments')
shopping_list = models.ForeignKey(ShoppingList, related_name='shopping_comments', on_delete=models.CASCADE, null=True),
game = models.ForeignKey(Game, related_name='game_comments', on_delete=models.CASCADE, null=True)
I could then query this game comments with something lile:
BigCommentModel.objects.filter(game__isnull=False)

Laravel Eloquent Filter By Column of Relationship

Using the Eloquent ORM I have my models set up like so: Post belongsToMany Category
Post.php
public function categories()
{
return $this->belongsToMany('Category', 'posts_categories');
}
I want to filter posts by a column of the categories relationship.
So I want to do something like:
$posts->where('categories.slug', '=', Input::get('category_slug'));
This doesn't work though.
I also tried:
$with['categories'] = function($query){
$query->where('slug', '=', Input::get('category_slug'));
};
$posts::with($with)->get();
But I thnk that's for filtering the categories not filtering BY the category.
Can anyone show me the way?
I can't access my Vagrant box right now, but I believe this should work:
$posts = Post::whereHas('categories', function($q)
{
$q->where('slug', '=', Input::get('category_slug'));
})->get();
Here's a quick way to filter on related model column:
$allConsultants = Consultant::whereHas('user', function($query)
{
$query->where('is_approved', '=', 1);
})->get();

Parent-child relationship with LINQ2SQL and POCO objects

I just started learning LINQ2SQL and one of the first things I wanted to try is a simple parent-child hierarchy, but I can't seem to find a good way to do it. I saw some examples here on SO and i've googled, but I couldn't apply them directly, so I'll explain exactly what i'm trying to accomplish.
Lets use the common example with tags.
Database tables: Post-- Post_Tags -- Tags
I've created a simple Post class so I avoid passing Linq2Sql classes around:
public class Post
{
public int Id {get; set;}
public int Title {get; set;}
public IEnumerable<string> Tags {get; set;}
}
I would like to select 5 latest records from the Posts table, get their related tags and return the IList where each Post has their Tags property filled.
Can you show me a concrete Linq2Sql code how could I do that?
I tried:
IList<Post> GetLatest()
{
return (from p in _db.Posts
orderby p.DateCreated descending
select new Post
{
Id = p.Id,
Title = p.Title,
Tags = p.Post_Tags.Select(pt => pt.Tag.Name)
}).Take(5).ToList();
}
This works but duplicates Post records for each Tag record and I have to duplicate property mapping (Id=p.Id, ...) in every method I user. I then tried this approach, but in this case, I have a roundtrip to DB for every tag:
IQueryable<Post> GetList()
{
return (from p in _db.Posts
select new Post
{
Id = p.Id,
Title = p.Title,
Tags = p.Post_Tags.Select(pt => pt.Tag.Name)
});
}
IList<Post> GetLatest()
{
return (from p in GetList()
orderby p.DateCreated descending
select p).Take(5).ToList();
}
If I were doing it in classic ADO.NET, I would create a stored procedure that returns two resultsets. One with Post records and second with related Tag records. I would then map them in the code (by hand, by DataRelation, ORM, etc.). Could I do the same with LINQ2SQL?
I'm really curious to see some code samples on how do you guys handle such simple hierarchies.
And yes, I would really like to return IList<> objects and my custom classes and not queryable Linq to Sql objects, because I would like to be flexible about the data access code if I for example decide to abandon Linq2Sql.
Thanks.
If you create a DataContext, the parent-child relationship is maintained automatically for you.
i.e. If you model the Posts and Tags and their relationship inside a Linq2Sql DataContext, you can then fetch posts like this:
var allPosts = from p in _db.Posts
orderby p.DateCreated descending
select p;
Then you won't have to worry about any tags at all, because they are accessible as a member of the variable p as in:
var allPostsList = allPosts.ToList();
var someTags = allPostsList[0].Post_Tags;
var moreTags = allPostsList[1].Post_Tags;
And then any repeated instance is then automatically updated across entire DataContext until you ask it to SubmitChanges();
IMO, That's the point of an ORM, you don't re-create the model class and maintain the mapping across many places because you want all those relationships managed for you by the ORM.
As for the roundtrip, if you refrain from any code that explicitly requests a trip to the database, all queries will be stored in an intermediate query representation and only when the data is actually needed to continue, is when the query will be translated to sql and dispatched to the database to fetch results.
i.e. the following code only access the database once
// these 3 variables are all in query form until otherwise needed
var allPosts = Posts.All();
var somePosts = allPosts.Where(p => p.Name.Contains("hello"));
var lesserPosts = somePosts.Where(p => p.Name.Contains("World"));
// calling "ToList" will force the query to be sent to the db
var result = lesserPosts.ToList();
How about if you set your DataLoadOptions first to explicitly load tags with posts? Something like:
IList<Post> GetLatest()
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Post>(post => post.Tags);
_db.LoadOptions = options;
return (from p in _db.Posts
orderby p.DateCreated descending)
Take(5).ToList();
}
List<Post> latestPosts = db.Posts
.OrderByDescending( p => p.DateCreated )
.Take(5)
.ToList();
// project the Posts to a List of IDs to send back in
List<int> postIDs = latestPosts
.Select(p => p.Id)
.ToList();
// fetch the strings and the ints used to connect
ILookup<int, string> tagNameLookup = db.Posts
.Where(p => postIDs.Contains(p.Id))
.SelectMany(p => p.Post_Tags)
.Select(pt => new {PostID = pt.PostID, TagName = pt.Tag.Name } )
.ToLookup(x => x.PostID, x => x.TagName);
//now form results
List<Post> results = latestPosts
.Select(p => new Post()
{
Id = p.Id,
Title = p.Title,
Tags = tagNameLookup[p.Id]
})
.ToList();