prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [CHORE] 졸업플래너 신규 model의 serialize결과 캐싱
  ## 동기
  
  **Is your feature request related to a problem? Please describe.**
  
  OTL의 주요 모델에는 캐시가 구현되어 있으나 졸업플래너에서 새로 생성된 model은 아직 캐싱이 적용되어 있지 않습니다.
  베타 출시 때는 우선 임시로 그대로 출시하였지만 캐시 도입이 필요합니다.
  특히 트랙 부분은 페이지 접속 시에 로딩되고 한번에 많은 양이 로드되기 때문에 성능이 상당히 저하될 여지가 있습니다.
  
  ## 설명
  
  **Describe the solution you'd like.**
  
  A clear and concise description of what you want to happen.
  
  ## 스크린샷
  
  (OPTIONAL) If applicable, add screenshots to help explain your feature request.
  
  ## 개발 환경
  
  - OS: [e.g. macOS]
  - ```python --version```:
  - ```node --version```:
  
  ## 테스트 환경
  
  (OPTIONAL)
  
  - Device: [e.g. iPhone6]
  - OS: [e.g. iOS8.1]
  - Web Version: [e.g. 1.1.0]
  
  ## 추가 정보
  
  (OPTIONAL) Add any other context or screenshots about the feature request here.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `apps/graduation/models.py`
  Content:
  ```
  1 from django.db import models
  2 
  3 from apps.subject.models import Department
  4 
  5 
  6 UNBOUND_START_YEAR = 2000
  7 UNBOUND_END_YEAR = 2100
  8 
  9 
  10 class GeneralTrack(models.Model):
  11     start_year = models.IntegerField(db_index=True)
  12     end_year = models.IntegerField(db_index=True)
  13     is_foreign = models.BooleanField(db_index=True)
  14 
  15     total_credit = models.IntegerField()
  16     total_au = models.IntegerField()
  17     basic_required = models.IntegerField()
  18     basic_elective = models.IntegerField()
  19     thesis_study = models.IntegerField()
  20     thesis_study_doublemajor = models.IntegerField()
  21     general_required_credit = models.IntegerField()
  22     general_required_au = models.IntegerField()
  23     humanities = models.IntegerField()
  24     humanities_doublemajor = models.IntegerField()
  25 
  26     class Meta:
  27         unique_together = [["start_year", "is_foreign"], ["end_year", "is_foreign"]]
  28 
  29     def to_json(self):
  30         result = {
  31             "id": self.id,
  32             "start_year": self.start_year,
  33             "end_year": self.end_year,
  34             "is_foreign": self.is_foreign,
  35             "total_credit": self.total_credit,
  36             "total_au": self.total_au,
  37             "basic_required": self.basic_required,
  38             "basic_elective": self.basic_elective,
  39             "thesis_study": self.thesis_study,
  40             "thesis_study_doublemajor": self.thesis_study_doublemajor,
  41             "general_required_credit": self.general_required_credit,
  42             "general_required_au": self.general_required_au,
  43             "humanities": self.humanities,
  44             "humanities_doublemajor": self.humanities_doublemajor,
  45         }
  46 
  47         return result
  48 
  49 
  50 class MajorTrack(models.Model):
  51     start_year = models.IntegerField(db_index=True)
  52     end_year = models.IntegerField(db_index=True)
  53     department = models.ForeignKey(Department,
  54                                    on_delete=models.CASCADE, db_index=True)
  55 
  56     basic_elective_doublemajor = models.IntegerField()
  57     major_required = models.IntegerField()
  58     major_elective = models.IntegerField()
  59 
  60     class Meta:
  61         unique_together = [["start_year", "department"], ["end_year", "department"]]
  62 
  63     def to_json(self):
  64         result = {
  65             "id": self.id,
  66             "start_year": self.start_year,
  67             "end_year": self.end_year,
  68             "department": self.department.to_json(nested=False),
  69             "basic_elective_doublemajor": self.basic_elective_doublemajor,
  70             "major_required": self.major_required,
  71             "major_elective": self.major_elective,
  72         }
  73 
  74         return result
  75 
  76 
  77 class AdditionalTrack(models.Model):
  78     ADDITIONAL_TYPE_CHOICES = [
  79         ('DOUBLE', 'DOUBLE'),
  80         ('MINOR', 'MINOR'),
  81         ('ADVANCED', 'ADVANCED'),
  82         ('INTERDISCIPLINARY', 'INTERDISCIPLINARY'),
  83     ]
  84 
  85     start_year = models.IntegerField(db_index=True)
  86     end_year = models.IntegerField(db_index=True)
  87     type = models.CharField(db_index=True, max_length=32, choices=ADDITIONAL_TYPE_CHOICES)
  88     department = models.ForeignKey(Department,
  89                                    null=True, blank=True,
  90                                    on_delete=models.CASCADE, db_index=True)
  91 
  92     major_required = models.IntegerField()
  93     major_elective = models.IntegerField()
  94 
  95     class Meta:
  96         unique_together = [["start_year", "type", "department"], ["end_year", "type", "department"]]
  97 
  98     def to_json(self):
  99         result = {
  100             "id": self.id,
  101             "start_year": self.start_year,
  102             "end_year": self.end_year,
  103             "type": self.type,
  104             "department": self.department.to_json(nested=False) if self.department else None,
  105             "major_required": self.major_required,
  106             "major_elective": self.major_elective,
  107         }
  108 
  109         return result
  110
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/apps/graduation/models.py b/apps/graduation/models.py
  --- a/apps/graduation/models.py
  +++ b/apps/graduation/models.py
  @@ -1,4 +1,5 @@
   from django.db import models
  +from django.core.cache import cache
   
   from apps.subject.models import Department
   
  @@ -26,7 +27,15 @@
       class Meta:
           unique_together = [["start_year", "is_foreign"], ["end_year", "is_foreign"]]
   
  +    def get_cache_key(self):
  +        return "generaltrack:%d-%d-%s" % (self.start_year, self.end_year, self.is_foreign)
  +
       def to_json(self):
  +        cache_id = self.get_cache_key()
  +        result_cached = cache.get(cache_id)
  +        if result_cached is not None:
  +            return result_cached
  +
           result = {
               "id": self.id,
               "start_year": self.start_year,
  @@ -44,6 +53,8 @@
               "humanities_doublemajor": self.humanities_doublemajor,
           }
   
  +        cache.set(cache_id, result, 60 * 60)
  +
           return result
   
   
  @@ -60,7 +71,15 @@
       class Meta:
           unique_together = [["start_year", "department"], ["end_year", "department"]]
   
  +    def get_cache_key(self):
  +        return "majortrack:%d-%d-%d" % (self.start_year, self.end_year, self.department.id)
  +
       def to_json(self):
  +        cache_id = self.get_cache_key()
  +        result_cached = cache.get(cache_id)
  +        if result_cached is not None:
  +            return result_cached
  +
           result = {
               "id": self.id,
               "start_year": self.start_year,
  @@ -71,6 +90,8 @@
               "major_elective": self.major_elective,
           }
   
  +        cache.set(cache_id, result, 60 * 60)
  +
           return result
   
   
  @@ -95,7 +116,15 @@
       class Meta:
           unique_together = [["start_year", "type", "department"], ["end_year", "type", "department"]]
   
  +    def get_cache_key(self):
  +        return "additionaltrack:%d-%d-%s-%d" % (self.start_year, self.end_year, self.type, self.department.id if self.department else 0)
  +
       def to_json(self):
  +        cache_id = self.get_cache_key()
  +        result_cached = cache.get(cache_id)
  +        if result_cached is not None:
  +            return result_cached
  +
           result = {
               "id": self.id,
               "start_year": self.start_year,
  @@ -106,4 +135,6 @@
               "major_elective": self.major_elective,
           }
   
  +        cache.set(cache_id, result, 60 * 60)
  +
           return result
  
verification_info: {"golden_diff": "diff --git a/apps/graduation/models.py b/apps/graduation/models.py\n--- a/apps/graduation/models.py\n+++ b/apps/graduation/models.py\n@@ -1,4 +1,5 @@\n from django.db import models\n+from django.core.cache import cache\n \n from apps.subject.models import Department\n \n@@ -26,7 +27,15 @@\n     class Meta:\n         unique_together = [[\"start_year\", \"is_foreign\"], [\"end_year\", \"is_foreign\"]]\n \n+    def get_cache_key(self):\n+        return \"generaltrack:%d-%d-%s\" % (self.start_year, self.end_year, self.is_foreign)\n+\n     def to_json(self):\n+        cache_id = self.get_cache_key()\n+        result_cached = cache.get(cache_id)\n+        if result_cached is not None:\n+            return result_cached\n+\n         result = {\n             \"id\": self.id,\n             \"start_year\": self.start_year,\n@@ -44,6 +53,8 @@\n             \"humanities_doublemajor\": self.humanities_doublemajor,\n         }\n \n+        cache.set(cache_id, result, 60 * 60)\n+\n         return result\n \n \n@@ -60,7 +71,15 @@\n     class Meta:\n         unique_together = [[\"start_year\", \"department\"], [\"end_year\", \"department\"]]\n \n+    def get_cache_key(self):\n+        return \"majortrack:%d-%d-%d\" % (self.start_year, self.end_year, self.department.id)\n+\n     def to_json(self):\n+        cache_id = self.get_cache_key()\n+        result_cached = cache.get(cache_id)\n+        if result_cached is not None:\n+            return result_cached\n+\n         result = {\n             \"id\": self.id,\n             \"start_year\": self.start_year,\n@@ -71,6 +90,8 @@\n             \"major_elective\": self.major_elective,\n         }\n \n+        cache.set(cache_id, result, 60 * 60)\n+\n         return result\n \n \n@@ -95,7 +116,15 @@\n     class Meta:\n         unique_together = [[\"start_year\", \"type\", \"department\"], [\"end_year\", \"type\", \"department\"]]\n \n+    def get_cache_key(self):\n+        return \"additionaltrack:%d-%d-%s-%d\" % (self.start_year, self.end_year, self.type, self.department.id if self.department else 0)\n+\n     def to_json(self):\n+        cache_id = self.get_cache_key()\n+        result_cached = cache.get(cache_id)\n+        if result_cached is not None:\n+            return result_cached\n+\n         result = {\n             \"id\": self.id,\n             \"start_year\": self.start_year,\n@@ -106,4 +135,6 @@\n             \"major_elective\": self.major_elective,\n         }\n \n+        cache.set(cache_id, result, 60 * 60)\n+\n         return result\n", "issue": "[CHORE] \uc878\uc5c5\ud50c\ub798\ub108 \uc2e0\uaddc model\uc758 serialize\uacb0\uacfc \uce90\uc2f1\n## \ub3d9\uae30\r\n\r\n**Is your feature request related to a problem? Please describe.**\r\n\r\nOTL\uc758 \uc8fc\uc694 \ubaa8\ub378\uc5d0\ub294 \uce90\uc2dc\uac00 \uad6c\ud604\ub418\uc5b4 \uc788\uc73c\ub098 \uc878\uc5c5\ud50c\ub798\ub108\uc5d0\uc11c \uc0c8\ub85c \uc0dd\uc131\ub41c model\uc740 \uc544\uc9c1 \uce90\uc2f1\uc774 \uc801\uc6a9\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.\r\n\ubca0\ud0c0 \ucd9c\uc2dc \ub54c\ub294 \uc6b0\uc120 \uc784\uc2dc\ub85c \uadf8\ub300\ub85c \ucd9c\uc2dc\ud558\uc600\uc9c0\ub9cc \uce90\uc2dc \ub3c4\uc785\uc774 \ud544\uc694\ud569\ub2c8\ub2e4.\r\n\ud2b9\ud788 \ud2b8\ub799 \ubd80\ubd84\uc740 \ud398\uc774\uc9c0 \uc811\uc18d \uc2dc\uc5d0 \ub85c\ub529\ub418\uace0 \ud55c\ubc88\uc5d0 \ub9ce\uc740 \uc591\uc774 \ub85c\ub4dc\ub418\uae30 \ub54c\ubb38\uc5d0 \uc131\ub2a5\uc774 \uc0c1\ub2f9\ud788 \uc800\ud558\ub420 \uc5ec\uc9c0\uac00 \uc788\uc2b5\ub2c8\ub2e4.\r\n\r\n## \uc124\uba85\r\n\r\n**Describe the solution you'd like.**\r\n\r\nA clear and concise description of what you want to happen.\r\n\r\n## \uc2a4\ud06c\ub9b0\uc0f7\r\n\r\n(OPTIONAL) If applicable, add screenshots to help explain your feature request.\r\n\r\n## \uac1c\ubc1c \ud658\uacbd\r\n\r\n- OS: [e.g. macOS]\r\n- ```python --version```:\r\n- ```node --version```:\r\n\r\n## \ud14c\uc2a4\ud2b8 \ud658\uacbd\r\n\r\n(OPTIONAL)\r\n\r\n- Device: [e.g. iPhone6]\r\n- OS: [e.g. iOS8.1]\r\n- Web Version: [e.g. 1.1.0]\r\n\r\n## \ucd94\uac00 \uc815\ubcf4\r\n\r\n(OPTIONAL) Add any other context or screenshots about the feature request here.\r\n\n", "before_files": [{"content": "from django.db import models\n\nfrom apps.subject.models import Department\n\n\nUNBOUND_START_YEAR = 2000\nUNBOUND_END_YEAR = 2100\n\n\nclass GeneralTrack(models.Model):\n    start_year = models.IntegerField(db_index=True)\n    end_year = models.IntegerField(db_index=True)\n    is_foreign = models.BooleanField(db_index=True)\n\n    total_credit = models.IntegerField()\n    total_au = models.IntegerField()\n    basic_required = models.IntegerField()\n    basic_elective = models.IntegerField()\n    thesis_study = models.IntegerField()\n    thesis_study_doublemajor = models.IntegerField()\n    general_required_credit = models.IntegerField()\n    general_required_au = models.IntegerField()\n    humanities = models.IntegerField()\n    humanities_doublemajor = models.IntegerField()\n\n    class Meta:\n        unique_together = [[\"start_year\", \"is_foreign\"], [\"end_year\", \"is_foreign\"]]\n\n    def to_json(self):\n        result = {\n            \"id\": self.id,\n            \"start_year\": self.start_year,\n            \"end_year\": self.end_year,\n            \"is_foreign\": self.is_foreign,\n            \"total_credit\": self.total_credit,\n            \"total_au\": self.total_au,\n            \"basic_required\": self.basic_required,\n            \"basic_elective\": self.basic_elective,\n            \"thesis_study\": self.thesis_study,\n            \"thesis_study_doublemajor\": self.thesis_study_doublemajor,\n            \"general_required_credit\": self.general_required_credit,\n            \"general_required_au\": self.general_required_au,\n            \"humanities\": self.humanities,\n            \"humanities_doublemajor\": self.humanities_doublemajor,\n        }\n\n        return result\n\n\nclass MajorTrack(models.Model):\n    start_year = models.IntegerField(db_index=True)\n    end_year = models.IntegerField(db_index=True)\n    department = models.ForeignKey(Department,\n                                   on_delete=models.CASCADE, db_index=True)\n\n    basic_elective_doublemajor = models.IntegerField()\n    major_required = models.IntegerField()\n    major_elective = models.IntegerField()\n\n    class Meta:\n        unique_together = [[\"start_year\", \"department\"], [\"end_year\", \"department\"]]\n\n    def to_json(self):\n        result = {\n            \"id\": self.id,\n            \"start_year\": self.start_year,\n            \"end_year\": self.end_year,\n            \"department\": self.department.to_json(nested=False),\n            \"basic_elective_doublemajor\": self.basic_elective_doublemajor,\n            \"major_required\": self.major_required,\n            \"major_elective\": self.major_elective,\n        }\n\n        return result\n\n\nclass AdditionalTrack(models.Model):\n    ADDITIONAL_TYPE_CHOICES = [\n        ('DOUBLE', 'DOUBLE'),\n        ('MINOR', 'MINOR'),\n        ('ADVANCED', 'ADVANCED'),\n        ('INTERDISCIPLINARY', 'INTERDISCIPLINARY'),\n    ]\n\n    start_year = models.IntegerField(db_index=True)\n    end_year = models.IntegerField(db_index=True)\n    type = models.CharField(db_index=True, max_length=32, choices=ADDITIONAL_TYPE_CHOICES)\n    department = models.ForeignKey(Department,\n                                   null=True, blank=True,\n                                   on_delete=models.CASCADE, db_index=True)\n\n    major_required = models.IntegerField()\n    major_elective = models.IntegerField()\n\n    class Meta:\n        unique_together = [[\"start_year\", \"type\", \"department\"], [\"end_year\", \"type\", \"department\"]]\n\n    def to_json(self):\n        result = {\n            \"id\": self.id,\n            \"start_year\": self.start_year,\n            \"end_year\": self.end_year,\n            \"type\": self.type,\n            \"department\": self.department.to_json(nested=False) if self.department else None,\n            \"major_required\": self.major_required,\n            \"major_elective\": self.major_elective,\n        }\n\n        return result\n", "path": "apps/graduation/models.py"}], "after_files": [{"content": "from django.db import models\nfrom django.core.cache import cache\n\nfrom apps.subject.models import Department\n\n\nUNBOUND_START_YEAR = 2000\nUNBOUND_END_YEAR = 2100\n\n\nclass GeneralTrack(models.Model):\n    start_year = models.IntegerField(db_index=True)\n    end_year = models.IntegerField(db_index=True)\n    is_foreign = models.BooleanField(db_index=True)\n\n    total_credit = models.IntegerField()\n    total_au = models.IntegerField()\n    basic_required = models.IntegerField()\n    basic_elective = models.IntegerField()\n    thesis_study = models.IntegerField()\n    thesis_study_doublemajor = models.IntegerField()\n    general_required_credit = models.IntegerField()\n    general_required_au = models.IntegerField()\n    humanities = models.IntegerField()\n    humanities_doublemajor = models.IntegerField()\n\n    class Meta:\n        unique_together = [[\"start_year\", \"is_foreign\"], [\"end_year\", \"is_foreign\"]]\n\n    def get_cache_key(self):\n        return \"generaltrack:%d-%d-%s\" % (self.start_year, self.end_year, self.is_foreign)\n\n    def to_json(self):\n        cache_id = self.get_cache_key()\n        result_cached = cache.get(cache_id)\n        if result_cached is not None:\n            return result_cached\n\n        result = {\n            \"id\": self.id,\n            \"start_year\": self.start_year,\n            \"end_year\": self.end_year,\n            \"is_foreign\": self.is_foreign,\n            \"total_credit\": self.total_credit,\n            \"total_au\": self.total_au,\n            \"basic_required\": self.basic_required,\n            \"basic_elective\": self.basic_elective,\n            \"thesis_study\": self.thesis_study,\n            \"thesis_study_doublemajor\": self.thesis_study_doublemajor,\n            \"general_required_credit\": self.general_required_credit,\n            \"general_required_au\": self.general_required_au,\n            \"humanities\": self.humanities,\n            \"humanities_doublemajor\": self.humanities_doublemajor,\n        }\n\n        cache.set(cache_id, result, 60 * 60)\n\n        return result\n\n\nclass MajorTrack(models.Model):\n    start_year = models.IntegerField(db_index=True)\n    end_year = models.IntegerField(db_index=True)\n    department = models.ForeignKey(Department,\n                                   on_delete=models.CASCADE, db_index=True)\n\n    basic_elective_doublemajor = models.IntegerField()\n    major_required = models.IntegerField()\n    major_elective = models.IntegerField()\n\n    class Meta:\n        unique_together = [[\"start_year\", \"department\"], [\"end_year\", \"department\"]]\n\n    def get_cache_key(self):\n        return \"majortrack:%d-%d-%d\" % (self.start_year, self.end_year, self.department.id)\n\n    def to_json(self):\n        cache_id = self.get_cache_key()\n        result_cached = cache.get(cache_id)\n        if result_cached is not None:\n            return result_cached\n\n        result = {\n            \"id\": self.id,\n            \"start_year\": self.start_year,\n            \"end_year\": self.end_year,\n            \"department\": self.department.to_json(nested=False),\n            \"basic_elective_doublemajor\": self.basic_elective_doublemajor,\n            \"major_required\": self.major_required,\n            \"major_elective\": self.major_elective,\n        }\n\n        cache.set(cache_id, result, 60 * 60)\n\n        return result\n\n\nclass AdditionalTrack(models.Model):\n    ADDITIONAL_TYPE_CHOICES = [\n        ('DOUBLE', 'DOUBLE'),\n        ('MINOR', 'MINOR'),\n        ('ADVANCED', 'ADVANCED'),\n        ('INTERDISCIPLINARY', 'INTERDISCIPLINARY'),\n    ]\n\n    start_year = models.IntegerField(db_index=True)\n    end_year = models.IntegerField(db_index=True)\n    type = models.CharField(db_index=True, max_length=32, choices=ADDITIONAL_TYPE_CHOICES)\n    department = models.ForeignKey(Department,\n                                   null=True, blank=True,\n                                   on_delete=models.CASCADE, db_index=True)\n\n    major_required = models.IntegerField()\n    major_elective = models.IntegerField()\n\n    class Meta:\n        unique_together = [[\"start_year\", \"type\", \"department\"], [\"end_year\", \"type\", \"department\"]]\n\n    def get_cache_key(self):\n        return \"additionaltrack:%d-%d-%s-%d\" % (self.start_year, self.end_year, self.type, self.department.id if self.department else 0)\n\n    def to_json(self):\n        cache_id = self.get_cache_key()\n        result_cached = cache.get(cache_id)\n        if result_cached is not None:\n            return result_cached\n\n        result = {\n            \"id\": self.id,\n            \"start_year\": self.start_year,\n            \"end_year\": self.end_year,\n            \"type\": self.type,\n            \"department\": self.department.to_json(nested=False) if self.department else None,\n            \"major_required\": self.major_required,\n            \"major_elective\": self.major_elective,\n        }\n\n        cache.set(cache_id, result, 60 * 60)\n\n        return result\n", "path": "apps/graduation/models.py"}]}