2015-09-03 11 views
13

के लिए एक JSON मानचित्र बनाना मैं इस तरह के रूप में एक Ecto मॉडल है:एक स्वयं को संदर्भित Ecto मॉडल

defmodule Project.Category do 
    use Project.Web, :model 

    schema "categories" do 
    field :name, :string 
    field :list_order, :integer 
    field :parent_id, :integer 
    belongs_to :menu, Project.Menu 
    has_many :subcategories, Project.Category, foreign_key: :parent_id 
    timestamps 
    end 

    @required_fields ~w(name list_order) 
    @optional_fields ~w(menu_id parent_id) 

    def changeset(model, params \\ :empty) do 
    model 
    |> cast(params, @required_fields, @optional_fields) 
    end 
end 

आप देख श्रेणी मॉडल उपश्रेणियाँ परमाणु के माध्यम से ही संदर्भित कर सकते हैं कर सकते हैं।

यहाँ इस मॉडल से संबद्ध दृश्य है:

defmodule Project.CategoryView do 
    use Project.Web, :view 

    def render("show.json", %{category: category}) do 
    json = %{ 
     id: category.id, 
     name: category.name, 
     list_order: category.list_order 
     parent_id: category.parent_id 
    } 
    if is_list(category.subcategories) do 
     children = render_many(category.subcategories, Project.CategoryView, "show.json") 
     Map.put(json, :subcategories, children) 
    else 
     json 
    end 
    end 
end 

ताकि मैं ज़हर के साथ अच्छा खेल सकते हैं, जब वे पहले से लोड नहीं कर रहे हैं मैं एक यदि उपश्रेणियाँ पर शर्त है।

अंत में, यहाँ मेरी 2 नियंत्रक कार्यों कि इस दृष्टिकोण को लागू कर रहे हैं:

defmodule Project.CategoryController do 
    use Project.Web, :controller 

    alias Project.Category 

    def show(conn, %{"id" => id}) do 
    category = Repo.get!(Category, id) 
    render conn, "show.json", category: category 
    end 

    def showWithChildren(conn, %{"id" => id}) do 
    category = Repo.get!(Category, id) 
       |> Repo.preload [:subcategories, subcategories: :subcategories] 
    render conn, "show.json", category: category 
    end 
end 

show समारोह ठीक काम करता है:

{ 
    "parent_id": null, 
    "name": "a", 
    "list_order": 4, 
    "id": 7 
} 

लेकिन, मेरा showWithChildren समारोह घोंसले की वजह से 2 स्तर तक ही सीमित है मैं प्रीलोडिंग का उपयोग कैसे करता हूं:

{ 
    "subcategories": [ 
    { 
     "subcategories": [ 
     { 
      "parent_id": 10, 
      "name": "d", 
      "list_order": 4, 
      "id": 11 
     } 
     ], 
     "parent_id": 7, 
     "name": "c", 
     "list_order": 4, 
     "id": 10 
    }, 
    { 
     "subcategories": [], 
     "parent_id": 7, 
     "name": "b", 
     "list_order": 9, 
     "id": 13 
    } 
    ], 
    "parent_id": null, 
    "name": "a", 
    "list_order": 4, 
    "id": 7 
} 

उदाहरण के लिए, उपरोक्त श्रेणी आइटम 11 में उपश्रेणियां भी हैं लेकिन मैं उन तक पहुंचने में असमर्थ हूं। उन उपश्रेणियों में उपश्रेणियां भी हो सकती हैं, इसलिए पदानुक्रम की संभावित गहराई एन है।

मुझे पता है कि मुझे कुछ रिकर्सिव जादू की आवश्यकता है, लेकिन चूंकि मैं कार्यात्मक प्रोग्रामिंग और एलिक्सीर दोनों के लिए नया हूं, इसलिए मैं अपने सिर को इसके चारों ओर लपेट नहीं सकता। कोई भी मदद बहुत ही सराहनीय होगी।

उत्तर

9

आप ध्यान में रखते हुए प्रीलोड करने पर विचार कर सकते हैं, तो यह रिकर्सिवली काम करता है:

  1. आदर्श रूप में आप नहीं चाहते: दो कारणों के लिए

    def render("show.json", %{category: category}) do 
        %{id: category.id, 
        name: category.name, 
        list_order: category.list_order 
        parent_id: category.parent_id} 
        |> add_subcategories(category) 
    end 
    
    defp add_subcategories(json, %{subcategories: subcategories}) when is_list(subcategories) do 
        children = 
        subcategories 
        |> Repo.preload(:subcategories) 
        |> render_many(Project.CategoryView, "show.json") 
        Map.put(json, :subcategories, children) 
    end 
    
    defp add_subcategories(json, _category) do 
        json 
    end 
    

    ध्यान रखें कि यह आदर्श नहीं है विचारों में प्रश्न पूछने के लिए (लेकिन यह रिकर्सिव है, इसलिए दृश्य प्रतिपादन में पिगबैक करना आसान है)

  2. आप दूसरे स्तर के लिए कई प्रश्नों को छोड़ने जा रहे हैं उपश्रेणियाँ

की एक किताब SQL Antipatterns कहा जाता है और, अगर मैं गलत नहीं हूँ, यह शामिल किया गया है कि कैसे वृक्ष संरचना लिखने के लिए। आपका उदाहरण मुफ्त अध्यायों में से एक में एंटीपेटर्न के रूप में उजागर किया गया है। यह एक उत्कृष्ट किताब है और वे सभी antipatterns के लिए समाधान का पता लगाने।

पीएस: आप show_with_children और showWithChildren नहीं चाहते हैं।

+2

अद्भुत काम करता है। :) यह उल्लेखनीय है कि परियोजना के रेपो को उपनाम के रूप में जोड़ा जाना चाहिए, क्योंकि दृश्य में उन्हें डिफ़ॉल्ट रूप से नहीं है। मैं आवेदन के बजाए डीबी पक्ष पर इसकी देखभाल करने के लिए देखता हूं। धन्यवाद! – user1112789

+0

@ जोस-वैलीम इस समस्या के लिए एक सुंदर विकल्प है? – user2290820

संबंधित मुद्दे