1

In my app that I am building to learn rails (RAILS 5), I have following end result I try to get in place:

I want users to be able to tag content of a PDF (attached to either an annotation record or a document record). After the annotation or document that the PDF is attached to has been classified by document type (e.g. PO, delivery note, etc.). A tag, when added, is associated with a predefined list of tag types (matching the document type of the annotation respectively of the document with the document type of the tag type). When adding the tag, I want to capture the content tagged (e.g. PO number or orderer name) and the coordinates in the PDF.

First part of the question

2 objects (classes / models) Annotation and Document have a 1-to-many relationship with the object (class / model) Tag (similar to order to order_item):

has_many :tags, dependent: :destroy

The model Tag has a 1-to-1 relationship with the model TagType:

belongs_to :annotation, :document

One single Tag-record however, can only belong to one Annotation / Document and needs to be deleted when the respective Annotation / Document gets deleted (I set dependent: :destroy for that).

So, which type of association to use for Tag with TagType? has_one? has_many? belongs_to_...?

Second part of the question:

Now, when adding a Tag to an Annotation or Document, the Tag will ge_ extracted text, coordinates and a needs to be assigned to a TagType. However, some tag types (for the document type of the annotation or document) can only be used once for an annotation / document - depending on the tag_type field "multiple occurrence" is false. How / where do I set this (validation / filter) up in the association?

And how to reduce the list depending on the tags in place (dynamically)?

All suggestions / directions welcome!

max
  • 96,212
  • 14
  • 104
  • 165
Dimitri de Ruiter
  • 725
  • 2
  • 7
  • 26

1 Answers1

1

For the first part:

For the association between Tag and Tag_type(TagType) model, you can use belongs_to in one model and has_one in the other. For example, Tag model can have belongs_to :tag_type, inverse_of: :tag and TagType model can have has_one :tag, inverse_of: :tag_type. Which model uses the belongs_to and which uses the has_one association will depend on the location of the foreign key. For example, the above code will work if the tags table has a tag_type_id column (The model with the belongs_to association should contain the id of the other table; look at this answer: https://stackoverflow.com/a/3808936/6006050).

For the second part:

I'm assuming there is no direct association between TagType model and Annotation or Document, and there is an association between Tag and Annotation/Document models, and between Tag and TagType models. Again, the validation should be in the model that contains the foreign key in the association between Tag and Annotation/Document. Since the association between Annotation/Document and Tag models is has_many, I'm assuming the tags table will hold the foreign key. In that case, the validation should be in the Tag model so that when you associate an Annotation/Document to the tag, the validation will fire.

Community
  • 1
  • 1
Reub
  • 392
  • 3
  • 8
  • On the second part - may be linguistic difference: yet on its creation I associate tag to an annotation / document. And then need to check if the tag_type for that tag: 1) is active, 2) has been used for that annotation/document and 3) its allowed occurrence (here). Am looking into validations doc of rails yet need some guidance/help. – Dimitri de Ruiter Sep 28 '16 at 09:02
  • In your Tag model, you can have `validate :some_function`. This will call `some_function` every time the object is saved. In `some_function`, you can perform all the checks that are required. If it fails any of your conditions, you should add an error to the self(tag) object, for example: `self.errors.add(:tag_type, "should be active")` – Reub Sep 28 '16 at 11:21
  • For is active, I found using scope works. Is scope something to research further for this? I.e. create a scope that check conditions 2 and 3. – Dimitri de Ruiter Sep 28 '16 at 12:03
  • A scope is useful if you will need to use it in multiple places. For condition 2, you don't need a scope, you can do something like `document.tag_types.include?(self.tag_type)` where document.tag_types can be a function that returns all the tag_types of that document. I didn't understand the 3rd condition. – Reub Sep 28 '16 at 12:41
  • Third condition: if a tag type has occur_once = true and has been used for that annotation or document it should be removed from the list of remaining tag types for that annotation or document – Dimitri de Ruiter Sep 28 '16 at 12:46
  • That doesn't sound like a validation. That is more of an action that needs to be taken after associating a new tag. You can do that using a `before_save` or `after_save` callback. DB data should usually not be modified in a validation since it may be run multiple times. A validation function should be idempotent. – Reub Sep 28 '16 at 13:00
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/124424/discussion-between-reub-and-dimitri-de-ruiter). – Reub Sep 28 '16 at 13:28
  • Am out of my office - on iPhone so bit hard – Dimitri de Ruiter Sep 28 '16 at 13:30
  • for conditions 1 and 2 (active tag types and matching document types) this works: '<%= f.association :tagtype, prompt: 'Select tag type', label: false, :collection => Tagtype.active.order(:name).where(:documenttype => @annotation.documenttype_id) %>' - left to resolve is condition 3 – Dimitri de Ruiter Oct 03 '16 at 09:28