martenframework / marten

The pragmatic web framework.
https://martenframework.com
MIT License
425 stars 24 forks source link

Ensure reverse query set creations are scoped to the related record #235

Closed ellmetha closed 5 months ago

ellmetha commented 5 months ago

Description

Right now it is possible to use the #create query set method as part of reverse query sets and create records that are actually not associated with the related record from which the query set was created.

For example, let's consider the following models:

class Author < Marten::Model
  field :id, :big_int, primary_key: true, auto: true
  field :name, :string, max_size: 128
end

class Book < Marten::Model
  field :id, :big_int, primary_key: true, auto: true
  field :title, :string, max_size: 128
  field :author, :many_to_one, to: Author, related: :books
end

Right now it is possible to create Book records through the books reverse query set that are not associated with the Author record from which the query set originated:

author_1 = Author.create!(name: "Author 1")
author_2 = Author.create!(name: "Author 2")

author_1.books.create!(title: "Book 1", author: author_2)

Proposition

Let's ensure that the records that are created from a reverse query sets like that are automatically scoped to the record from which the query set originated.

For example:

author_1 = Author.create!(name: "Author 1")
author_2 = Author.create!(name: "Author 2")

new_book = author_1.books.create!(title: "Book 1")
new_book.author # => author_1

We should also ensure that overriding the related record explicitly is forbidden. When this happens, a dedicated exception (Marten::DB::Errors::UnmetQuerySetCondition) should be raised:

author_1 = Author.create!(name: "Author 1")
author_2 = Author.create!(name: "Author 2")

author_1.books.create!(title: "Book 1", author: author_2) # => Raises Marten::DB::Errors::UnmetQuerySetCondition