简体   繁体   中英

Sum of attributes in model with has_many :through association not working in Rails 5

So I have 2 models. Meal & Food. A meal can hav multiple food items and a food item can be a part of many meals. Basically a many-to-many association. I did that with a has_many :through and the joining model is called MealsFood. When creating a new meal you choose via checkboxes what food items you want to add. The foods table has a column called "calories" and the meals table has a column called "total_calories" where it calculates the sum of all the food items in the meal. The problem is that it isn't working.

Here's what I have so far...

Models

class Meal < ApplicationRecord
    belongs_to :user, optional: true
    has_many :meal_foods
    has_many :foods, through: :meal_foods

    def calc_total_calories
        self.foods.sum(:calories)
    end
end

class MealFood < ApplicationRecord
  belongs_to :meal
  belongs_to :food
end

class Food < ApplicationRecord
    has_many :meal_foods
    has_many :meals, through: :meal_foods
end

Meals Controller

class MealsController < ApplicationController
    def index
    end

    def new
        @meal = Meal.new
    end

    def create
        @meal = Meal.new(meal_params)
        @meal.calc_total_calories

        if @meal.save
            redirect_to @meal
        else
            redirect_to root_path
        end
    end

    private

    def meal_params
        params.require(:meal).permit(:user_id, :meal_type, :date, 
  :total_calories, :total_carbohydrates, :total_fat, food_ids:[])
    end
end

View (new action for meals)

            <%= form_for(@meal) do |f| %>
                <div class="field">
                    <%= f.label :meal_type %>
                    <%= f.select :meal_type, ["Breakfast", "Lunch", "Dinner", "Morning Snack", "Afternoon Snack, Evening Snack"] %>
                </div>

                <div class="field">
                    <% Food.all.each do |food| %>
                        <%= check_box_tag "meal[food_ids][]", food.id %>
                        <%= food.name %>
                    <% end %>
                </div>

                <div class="field">
                    <%= f.submit class: "button button-highlight button-block" %>
                </div>
            <% end %>

Notice the def calc_total_calories in the Meal model. It's what I use to calculate the calories, but it doesn't work. I use it in the create method in the Meals controller. Please help! Thanks in advance :)

It doesn't look like you're actually doing anything with calc_total_calories - it's not assigned to anything in the controller, and not assigning to anything in the model.

You likely want the model method to assign it to something, as in the following:

def calc_total_calories
    self.total_calories = self.foods.sum(:calories)
end

That assumes a column on your meal model called total_calories .

Does that seem like what you're looking for? Let me know if you've any questions.

I'm thinking, perhaps, that you want to do something like:

class MealsController < ApplicationController

  def index
  end

  def new
      @meal = Meal.new
  end

  def create
    @meal = Meal.new(meal_params)

    if @meal.save
      @meal.update(total_calories: @meal.calc_total_calories)
      redirect_to @meal
    else
      redirect_to root_path
    end
  end

  private

  def meal_params
    params.require(:meal).permit(:user_id, :meal_type, :date, :total_calories, :total_carbohydrates, :total_fat, food_ids:[])
  end

end

In your original code, you're not setting the total_calories attribute on @meal , just calling the calc_total_calories method (which is probably calculating total calories correctly).

In the answer provided by SRack, you're setting, but never saving the total_calories attribute.

update will both set and save the attribute.

BTW, you'll want to be sure to think about how you handle changing the foods associated with a meal or else your total_calories may become stale and incorrect.

You could update total calories after saving, in a callback.

after_save :update_calories

def update_calories
  update_attributes(total_calories: calc_total_calories)
  return true
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM