[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-sentry-django-perf-review":3,"mdc-pjmqhp-key":38,"related-org-sentry-django-perf-review":2351,"related-repo-sentry-django-perf-review":2516},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":33,"sourceUrl":36,"mdContent":37},"django-perf-review","review and optimize Django performance","Django performance code review. Use when asked to \"review Django performance\", \"find N+1 queries\", \"optimize Django\", \"check queryset performance\", \"database performance\", \"Django ORM issues\", or audit Django code for performance problems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"sentry","Sentry","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fsentry.png","getsentry",[13,17,20,23],{"name":14,"slug":15,"type":16},"Performance","performance","tag",{"name":18,"slug":19,"type":16},"Code Review","code-review",{"name":21,"slug":22,"type":16},"Database","database",{"name":24,"slug":25,"type":16},"Django","django",861,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fskills","2026-05-15T06:16:24.832813","LICENSE",45,[32],"tag-production",{"repoUrl":27,"stars":26,"forks":30,"topics":34,"description":35},[32],"Agent Skills used by the Sentry team for development.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fskills\u002Ftree\u002FHEAD\u002Fskills\u002Fdjango-perf-review","---\nname: django-perf-review\ndescription: Django performance code review. Use when asked to \"review Django performance\", \"find N+1 queries\", \"optimize Django\", \"check queryset performance\", \"database performance\", \"Django ORM issues\", or audit Django code for performance problems.\nallowed-tools: Read, Grep, Glob, Bash, Task\nlicense: LICENSE\n---\n\n# Django Performance Review\n\nReview Django code for **validated** performance issues. Research the codebase to confirm issues before reporting. Report only what you can prove.\n\n## Review Approach\n\n1. **Research first** - Trace data flow, check for existing optimizations, verify data volume\n2. **Validate before reporting** - Pattern matching is not validation\n3. **Zero findings is acceptable** - Don't manufacture issues to appear thorough\n4. **Severity must match impact** - If you catch yourself writing \"minor\" in a CRITICAL finding, it's not critical. Downgrade or skip it.\n\n## Impact Categories\n\nIssues are organized by impact. Focus on CRITICAL and HIGH - these cause real problems at scale.\n\n| Priority | Category | Impact |\n|----------|----------|--------|\n| 1 | N+1 Queries | **CRITICAL** - Multiplies with data, causes timeouts |\n| 2 | Unbounded Querysets | **CRITICAL** - Memory exhaustion, OOM kills |\n| 3 | Missing Indexes | **HIGH** - Full table scans on large tables |\n| 4 | Write Loops | **HIGH** - Lock contention, slow requests |\n| 5 | Inefficient Patterns | **LOW** - Rarely worth reporting |\n\n---\n\n## Priority 1: N+1 Queries (CRITICAL)\n\n**Impact:** Each N+1 adds `O(n)` database round trips. 100 rows = 100 extra queries. 10,000 rows = timeout.\n\n### Rule: Prefetch related data accessed in loops\n\nValidate by tracing: View → Queryset → Template\u002FSerializer → Loop access\n\n```python\n# PROBLEM: N+1 - each iteration queries profile\ndef user_list(request):\n    users = User.objects.all()\n    return render(request, 'users.html', {'users': users})\n\n# Template:\n# {% for user in users %}\n#     {{ user.profile.bio }}  ← triggers query per user\n# {% endfor %}\n\n# SOLUTION: Prefetch in view\ndef user_list(request):\n    users = User.objects.select_related('profile')\n    return render(request, 'users.html', {'users': users})\n```\n\n### Rule: Prefetch in serializers, not just views\n\nDRF serializers accessing related fields cause N+1 if queryset isn't optimized.\n\n```python\n# PROBLEM: SerializerMethodField queries per object\nclass UserSerializer(serializers.ModelSerializer):\n    order_count = serializers.SerializerMethodField()\n\n    def get_order_count(self, obj):\n        return obj.orders.count()  # ← query per user\n\n# SOLUTION: Annotate in viewset, access in serializer\nclass UserViewSet(viewsets.ModelViewSet):\n    def get_queryset(self):\n        return User.objects.annotate(order_count=Count('orders'))\n\nclass UserSerializer(serializers.ModelSerializer):\n    order_count = serializers.IntegerField(read_only=True)\n```\n\n### Rule: Model properties that query are dangerous in loops\n\n```python\n# PROBLEM: Property triggers query when accessed\nclass User(models.Model):\n    @property\n    def recent_orders(self):\n        return self.orders.filter(created__gte=last_week)[:5]\n\n# Used in template loop = N+1\n\n# SOLUTION: Use Prefetch with custom queryset, or annotate\n```\n\n### Validation Checklist for N+1\n- [ ] Traced data flow from view to template\u002Fserializer\n- [ ] Confirmed related field is accessed inside a loop\n- [ ] Searched codebase for existing select_related\u002Fprefetch_related\n- [ ] Verified table has significant row count (1000+)\n- [ ] Confirmed this is a hot path (not admin, not rare action)\n\n---\n\n## Priority 2: Unbounded Querysets (CRITICAL)\n\n**Impact:** Loading entire tables exhausts memory. Large tables cause OOM kills and worker restarts.\n\n### Rule: Always paginate list endpoints\n\n```python\n# PROBLEM: No pagination - loads all rows\nclass UserListView(ListView):\n    model = User\n    template_name = 'users.html'\n\n# SOLUTION: Add pagination\nclass UserListView(ListView):\n    model = User\n    template_name = 'users.html'\n    paginate_by = 25\n```\n\n### Rule: Use iterator() for large batch processing\n\n```python\n# PROBLEM: Loads all objects into memory at once\nfor user in User.objects.all():\n    process(user)\n\n# SOLUTION: Stream with iterator()\nfor user in User.objects.iterator(chunk_size=1000):\n    process(user)\n```\n\n### Rule: Never call list() on unbounded querysets\n\n```python\n# PROBLEM: Forces full evaluation into memory\nall_users = list(User.objects.all())\n\n# SOLUTION: Keep as queryset, slice if needed\nusers = User.objects.all()[:100]\n```\n\n### Validation Checklist for Unbounded Querysets\n- [ ] Table is large (10k+ rows) or will grow unbounded\n- [ ] No pagination class, paginate_by, or slicing\n- [ ] This runs on user-facing request (not background job with chunking)\n\n---\n\n## Priority 3: Missing Indexes (HIGH)\n\n**Impact:** Full table scans. Negligible on small tables, catastrophic on large ones.\n\n### Rule: Index fields used in WHERE clauses on large tables\n\n```python\n# PROBLEM: Filtering on unindexed field\n# User.objects.filter(email=email)  # full scan if no index\n\nclass User(models.Model):\n    email = models.EmailField()  # ← no db_index\n\n# SOLUTION: Add index\nclass User(models.Model):\n    email = models.EmailField(db_index=True)\n```\n\n### Rule: Index fields used in ORDER BY on large tables\n\n```python\n# PROBLEM: Sorting requires full scan without index\nOrder.objects.order_by('-created')\n\n# SOLUTION: Index the sort field\nclass Order(models.Model):\n    created = models.DateTimeField(db_index=True)\n```\n\n### Rule: Use composite indexes for common query patterns\n\n```python\nclass Order(models.Model):\n    user = models.ForeignKey(User)\n    status = models.CharField(max_length=20)\n    created = models.DateTimeField()\n\n    class Meta:\n        indexes = [\n            models.Index(fields=['user', 'status']),  # for filter(user=x, status=y)\n            models.Index(fields=['status', '-created']),  # for filter(status=x).order_by('-created')\n        ]\n```\n\n### Validation Checklist for Missing Indexes\n- [ ] Table has 10k+ rows\n- [ ] Field is used in filter() or order_by() on hot path\n- [ ] Checked model - no db_index=True or Meta.indexes entry\n- [ ] Not a foreign key (already indexed automatically)\n\n---\n\n## Priority 4: Write Loops (HIGH)\n\n**Impact:** N database writes instead of 1. Lock contention. Slow requests.\n\n### Rule: Use bulk_create instead of create() in loops\n\n```python\n# PROBLEM: N inserts, N round trips\nfor item in items:\n    Model.objects.create(name=item['name'])\n\n# SOLUTION: Single bulk insert\nModel.objects.bulk_create([\n    Model(name=item['name']) for item in items\n])\n```\n\n### Rule: Use update() or bulk_update instead of save() in loops\n\n```python\n# PROBLEM: N updates\nfor obj in queryset:\n    obj.status = 'done'\n    obj.save()\n\n# SOLUTION A: Single UPDATE statement (same value for all)\nqueryset.update(status='done')\n\n# SOLUTION B: bulk_update (different values)\nfor obj in objects:\n    obj.status = compute_status(obj)\nModel.objects.bulk_update(objects, ['status'], batch_size=500)\n```\n\n### Rule: Use delete() on queryset, not in loops\n\n```python\n# PROBLEM: N deletes\nfor obj in queryset:\n    obj.delete()\n\n# SOLUTION: Single DELETE\nqueryset.delete()\n```\n\n### Validation Checklist for Write Loops\n- [ ] Loop iterates over 100+ items (or unbounded)\n- [ ] Each iteration calls create(), save(), or delete()\n- [ ] This runs on user-facing request (not one-time migration script)\n\n---\n\n## Priority 5: Inefficient Patterns (LOW)\n\n**Rarely worth reporting.** Include only as minor notes if you're already reporting real issues.\n\n### Pattern: count() vs exists()\n\n```python\n# Slightly suboptimal\nif queryset.count() > 0:\n    do_thing()\n\n# Marginally better\nif queryset.exists():\n    do_thing()\n```\n\n**Usually skip** - difference is \u003C1ms in most cases.\n\n### Pattern: len(queryset) vs count()\n\n```python\n# Fetches all rows to count\nif len(queryset) > 0:  # bad if queryset not yet evaluated\n\n# Single COUNT query\nif queryset.count() > 0:\n```\n\n**Only flag** if queryset is large and not already evaluated.\n\n### Pattern: get() in small loops\n\n```python\n# N queries, but if N is small (\u003C 20), often fine\nfor id in ids:\n    obj = Model.objects.get(id=id)\n```\n\n**Only flag** if loop is large or this is in a very hot path.\n\n---\n\n## Validation Requirements\n\nBefore reporting ANY issue:\n\n1. **Trace the data flow** - Follow queryset from creation to consumption\n2. **Search for existing optimizations** - Grep for select_related, prefetch_related, pagination\n3. **Verify data volume** - Check if table is actually large\n4. **Confirm hot path** - Trace call sites, verify this runs frequently\n5. **Rule out mitigations** - Check for caching, rate limiting\n\n**If you cannot validate all steps, do not report.**\n\n---\n\n## Output Format\n\n```markdown\n## Django Performance Review: [File\u002FComponent Name]\n\n### Summary\nValidated issues: X (Y Critical, Z High)\n\n### Findings\n\n#### [PERF-001] N+1 Query in UserListView (CRITICAL)\n**Location:** `views.py:45`\n\n**Issue:** Related field `profile` accessed in template loop without prefetch.\n\n**Validation:**\n- Traced: UserListView → users queryset → user_list.html → `{{ user.profile.bio }}` in loop\n- Searched codebase: no select_related('profile') found\n- User table: 50k+ rows (verified in admin)\n- Hot path: linked from homepage navigation\n\n**Evidence:**\n```python\ndef get_queryset(self):\n    return User.objects.filter(active=True)  # no select_related\n```\n\n**Fix:**\n```python\ndef get_queryset(self):\n    return User.objects.filter(active=True).select_related('profile')\n```\n```\n\nIf no issues found: \"No performance issues identified after reviewing [files] and validating [what you checked].\"\n\n**Before submitting, sanity check each finding:**\n- Does the severity match the actual impact? (\"Minor inefficiency\" ≠ CRITICAL)\n- Is this a real performance issue or just a style preference?\n- Would fixing this measurably improve performance?\n\nIf the answer to any is \"no\" - remove the finding.\n\n---\n\n## What NOT to Report\n\n- Test files\n- Admin-only views\n- Management commands\n- Migration files\n- One-time scripts\n- Code behind disabled feature flags\n- Tables with \u003C1000 rows that won't grow\n- Patterns in cold paths (rarely executed code)\n- Micro-optimizations (exists vs count, only\u002Fdefer without evidence)\n\n### False Positives to Avoid\n\n**Queryset variable assignment is not an issue:**\n```python\n# This is FINE - no performance difference\nprojects_qs = Project.objects.filter(org=org)\nprojects = list(projects_qs)\n\n# vs this - identical performance\nprojects = list(Project.objects.filter(org=org))\n```\nQuerysets are lazy. Assigning to a variable doesn't execute anything.\n\n**Single query patterns are not N+1:**\n```python\n# This is ONE query, not N+1\nprojects = list(Project.objects.filter(org=org))\n```\nN+1 requires a loop that triggers additional queries. A single `list()` call is fine.\n\n**Missing select_related on single object fetch is not N+1:**\n```python\n# This is 2 queries, not N+1 - report as LOW at most\nstate = AutofixState.objects.filter(pr_id=pr_id).first()\nproject_id = state.request.project_id  # second query\n```\nN+1 requires a loop. A single object doing 2 queries instead of 1 can be reported as LOW if relevant, but never as CRITICAL\u002FHIGH.\n\n**Style preferences are not performance issues:**\nIf your only suggestion is \"combine these two lines\" or \"rename this variable\" - that's style, not performance. Don't report it.\n",{"data":39,"body":41},{"name":4,"description":6,"allowed-tools":40,"license":29},"Read, Grep, Glob, Bash, Task",{"type":42,"children":43},"root",[44,53,67,74,119,125,130,276,280,286,305,312,317,454,460,465,580,586,663,669,723,726,732,741,747,830,836,897,903,949,955,986,989,995,1004,1010,1085,1091,1145,1151,1236,1242,1282,1285,1291,1300,1306,1376,1382,1483,1489,1542,1548,1579,1582,1588,1598,1604,1665,1675,1681,1726,1736,1742,1773,1782,1785,1791,1796,1849,1857,1860,1866,2202,2210,2232,2242,2247,2255,2278,2291,2299,2330,2335,2345],{"type":45,"tag":46,"props":47,"children":49},"element","h1",{"id":48},"django-performance-review",[50],{"type":51,"value":52},"text","Django Performance Review",{"type":45,"tag":54,"props":55,"children":56},"p",{},[57,59,65],{"type":51,"value":58},"Review Django code for ",{"type":45,"tag":60,"props":61,"children":62},"strong",{},[63],{"type":51,"value":64},"validated",{"type":51,"value":66}," performance issues. Research the codebase to confirm issues before reporting. Report only what you can prove.",{"type":45,"tag":68,"props":69,"children":71},"h2",{"id":70},"review-approach",[72],{"type":51,"value":73},"Review Approach",{"type":45,"tag":75,"props":76,"children":77},"ol",{},[78,89,99,109],{"type":45,"tag":79,"props":80,"children":81},"li",{},[82,87],{"type":45,"tag":60,"props":83,"children":84},{},[85],{"type":51,"value":86},"Research first",{"type":51,"value":88}," - Trace data flow, check for existing optimizations, verify data volume",{"type":45,"tag":79,"props":90,"children":91},{},[92,97],{"type":45,"tag":60,"props":93,"children":94},{},[95],{"type":51,"value":96},"Validate before reporting",{"type":51,"value":98}," - Pattern matching is not validation",{"type":45,"tag":79,"props":100,"children":101},{},[102,107],{"type":45,"tag":60,"props":103,"children":104},{},[105],{"type":51,"value":106},"Zero findings is acceptable",{"type":51,"value":108}," - Don't manufacture issues to appear thorough",{"type":45,"tag":79,"props":110,"children":111},{},[112,117],{"type":45,"tag":60,"props":113,"children":114},{},[115],{"type":51,"value":116},"Severity must match impact",{"type":51,"value":118}," - If you catch yourself writing \"minor\" in a CRITICAL finding, it's not critical. Downgrade or skip it.",{"type":45,"tag":68,"props":120,"children":122},{"id":121},"impact-categories",[123],{"type":51,"value":124},"Impact Categories",{"type":45,"tag":54,"props":126,"children":127},{},[128],{"type":51,"value":129},"Issues are organized by impact. Focus on CRITICAL and HIGH - these cause real problems at scale.",{"type":45,"tag":131,"props":132,"children":133},"table",{},[134,158],{"type":45,"tag":135,"props":136,"children":137},"thead",{},[138],{"type":45,"tag":139,"props":140,"children":141},"tr",{},[142,148,153],{"type":45,"tag":143,"props":144,"children":145},"th",{},[146],{"type":51,"value":147},"Priority",{"type":45,"tag":143,"props":149,"children":150},{},[151],{"type":51,"value":152},"Category",{"type":45,"tag":143,"props":154,"children":155},{},[156],{"type":51,"value":157},"Impact",{"type":45,"tag":159,"props":160,"children":161},"tbody",{},[162,186,208,231,253],{"type":45,"tag":139,"props":163,"children":164},{},[165,171,176],{"type":45,"tag":166,"props":167,"children":168},"td",{},[169],{"type":51,"value":170},"1",{"type":45,"tag":166,"props":172,"children":173},{},[174],{"type":51,"value":175},"N+1 Queries",{"type":45,"tag":166,"props":177,"children":178},{},[179,184],{"type":45,"tag":60,"props":180,"children":181},{},[182],{"type":51,"value":183},"CRITICAL",{"type":51,"value":185}," - Multiplies with data, causes timeouts",{"type":45,"tag":139,"props":187,"children":188},{},[189,194,199],{"type":45,"tag":166,"props":190,"children":191},{},[192],{"type":51,"value":193},"2",{"type":45,"tag":166,"props":195,"children":196},{},[197],{"type":51,"value":198},"Unbounded Querysets",{"type":45,"tag":166,"props":200,"children":201},{},[202,206],{"type":45,"tag":60,"props":203,"children":204},{},[205],{"type":51,"value":183},{"type":51,"value":207}," - Memory exhaustion, OOM kills",{"type":45,"tag":139,"props":209,"children":210},{},[211,216,221],{"type":45,"tag":166,"props":212,"children":213},{},[214],{"type":51,"value":215},"3",{"type":45,"tag":166,"props":217,"children":218},{},[219],{"type":51,"value":220},"Missing Indexes",{"type":45,"tag":166,"props":222,"children":223},{},[224,229],{"type":45,"tag":60,"props":225,"children":226},{},[227],{"type":51,"value":228},"HIGH",{"type":51,"value":230}," - Full table scans on large tables",{"type":45,"tag":139,"props":232,"children":233},{},[234,239,244],{"type":45,"tag":166,"props":235,"children":236},{},[237],{"type":51,"value":238},"4",{"type":45,"tag":166,"props":240,"children":241},{},[242],{"type":51,"value":243},"Write Loops",{"type":45,"tag":166,"props":245,"children":246},{},[247,251],{"type":45,"tag":60,"props":248,"children":249},{},[250],{"type":51,"value":228},{"type":51,"value":252}," - Lock contention, slow requests",{"type":45,"tag":139,"props":254,"children":255},{},[256,261,266],{"type":45,"tag":166,"props":257,"children":258},{},[259],{"type":51,"value":260},"5",{"type":45,"tag":166,"props":262,"children":263},{},[264],{"type":51,"value":265},"Inefficient Patterns",{"type":45,"tag":166,"props":267,"children":268},{},[269,274],{"type":45,"tag":60,"props":270,"children":271},{},[272],{"type":51,"value":273},"LOW",{"type":51,"value":275}," - Rarely worth reporting",{"type":45,"tag":277,"props":278,"children":279},"hr",{},[],{"type":45,"tag":68,"props":281,"children":283},{"id":282},"priority-1-n1-queries-critical",[284],{"type":51,"value":285},"Priority 1: N+1 Queries (CRITICAL)",{"type":45,"tag":54,"props":287,"children":288},{},[289,294,296,303],{"type":45,"tag":60,"props":290,"children":291},{},[292],{"type":51,"value":293},"Impact:",{"type":51,"value":295}," Each N+1 adds ",{"type":45,"tag":297,"props":298,"children":300},"code",{"className":299},[],[301],{"type":51,"value":302},"O(n)",{"type":51,"value":304}," database round trips. 100 rows = 100 extra queries. 10,000 rows = timeout.",{"type":45,"tag":306,"props":307,"children":309},"h3",{"id":308},"rule-prefetch-related-data-accessed-in-loops",[310],{"type":51,"value":311},"Rule: Prefetch related data accessed in loops",{"type":45,"tag":54,"props":313,"children":314},{},[315],{"type":51,"value":316},"Validate by tracing: View → Queryset → Template\u002FSerializer → Loop access",{"type":45,"tag":318,"props":319,"children":324},"pre",{"className":320,"code":321,"language":322,"meta":323,"style":323},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# PROBLEM: N+1 - each iteration queries profile\ndef user_list(request):\n    users = User.objects.all()\n    return render(request, 'users.html', {'users': users})\n\n# Template:\n# {% for user in users %}\n#     {{ user.profile.bio }}  ← triggers query per user\n# {% endfor %}\n\n# SOLUTION: Prefetch in view\ndef user_list(request):\n    users = User.objects.select_related('profile')\n    return render(request, 'users.html', {'users': users})\n","python","",[325],{"type":45,"tag":297,"props":326,"children":327},{"__ignoreMap":323},[328,339,348,357,366,376,385,394,403,412,420,429,437,446],{"type":45,"tag":329,"props":330,"children":333},"span",{"class":331,"line":332},"line",1,[334],{"type":45,"tag":329,"props":335,"children":336},{},[337],{"type":51,"value":338},"# PROBLEM: N+1 - each iteration queries profile\n",{"type":45,"tag":329,"props":340,"children":342},{"class":331,"line":341},2,[343],{"type":45,"tag":329,"props":344,"children":345},{},[346],{"type":51,"value":347},"def user_list(request):\n",{"type":45,"tag":329,"props":349,"children":351},{"class":331,"line":350},3,[352],{"type":45,"tag":329,"props":353,"children":354},{},[355],{"type":51,"value":356},"    users = User.objects.all()\n",{"type":45,"tag":329,"props":358,"children":360},{"class":331,"line":359},4,[361],{"type":45,"tag":329,"props":362,"children":363},{},[364],{"type":51,"value":365},"    return render(request, 'users.html', {'users': users})\n",{"type":45,"tag":329,"props":367,"children":369},{"class":331,"line":368},5,[370],{"type":45,"tag":329,"props":371,"children":373},{"emptyLinePlaceholder":372},true,[374],{"type":51,"value":375},"\n",{"type":45,"tag":329,"props":377,"children":379},{"class":331,"line":378},6,[380],{"type":45,"tag":329,"props":381,"children":382},{},[383],{"type":51,"value":384},"# Template:\n",{"type":45,"tag":329,"props":386,"children":388},{"class":331,"line":387},7,[389],{"type":45,"tag":329,"props":390,"children":391},{},[392],{"type":51,"value":393},"# {% for user in users %}\n",{"type":45,"tag":329,"props":395,"children":397},{"class":331,"line":396},8,[398],{"type":45,"tag":329,"props":399,"children":400},{},[401],{"type":51,"value":402},"#     {{ user.profile.bio }}  ← triggers query per user\n",{"type":45,"tag":329,"props":404,"children":406},{"class":331,"line":405},9,[407],{"type":45,"tag":329,"props":408,"children":409},{},[410],{"type":51,"value":411},"# {% endfor %}\n",{"type":45,"tag":329,"props":413,"children":415},{"class":331,"line":414},10,[416],{"type":45,"tag":329,"props":417,"children":418},{"emptyLinePlaceholder":372},[419],{"type":51,"value":375},{"type":45,"tag":329,"props":421,"children":423},{"class":331,"line":422},11,[424],{"type":45,"tag":329,"props":425,"children":426},{},[427],{"type":51,"value":428},"# SOLUTION: Prefetch in view\n",{"type":45,"tag":329,"props":430,"children":432},{"class":331,"line":431},12,[433],{"type":45,"tag":329,"props":434,"children":435},{},[436],{"type":51,"value":347},{"type":45,"tag":329,"props":438,"children":440},{"class":331,"line":439},13,[441],{"type":45,"tag":329,"props":442,"children":443},{},[444],{"type":51,"value":445},"    users = User.objects.select_related('profile')\n",{"type":45,"tag":329,"props":447,"children":449},{"class":331,"line":448},14,[450],{"type":45,"tag":329,"props":451,"children":452},{},[453],{"type":51,"value":365},{"type":45,"tag":306,"props":455,"children":457},{"id":456},"rule-prefetch-in-serializers-not-just-views",[458],{"type":51,"value":459},"Rule: Prefetch in serializers, not just views",{"type":45,"tag":54,"props":461,"children":462},{},[463],{"type":51,"value":464},"DRF serializers accessing related fields cause N+1 if queryset isn't optimized.",{"type":45,"tag":318,"props":466,"children":468},{"className":320,"code":467,"language":322,"meta":323,"style":323},"# PROBLEM: SerializerMethodField queries per object\nclass UserSerializer(serializers.ModelSerializer):\n    order_count = serializers.SerializerMethodField()\n\n    def get_order_count(self, obj):\n        return obj.orders.count()  # ← query per user\n\n# SOLUTION: Annotate in viewset, access in serializer\nclass UserViewSet(viewsets.ModelViewSet):\n    def get_queryset(self):\n        return User.objects.annotate(order_count=Count('orders'))\n\nclass UserSerializer(serializers.ModelSerializer):\n    order_count = serializers.IntegerField(read_only=True)\n",[469],{"type":45,"tag":297,"props":470,"children":471},{"__ignoreMap":323},[472,480,488,496,503,511,519,526,534,542,550,558,565,572],{"type":45,"tag":329,"props":473,"children":474},{"class":331,"line":332},[475],{"type":45,"tag":329,"props":476,"children":477},{},[478],{"type":51,"value":479},"# PROBLEM: SerializerMethodField queries per object\n",{"type":45,"tag":329,"props":481,"children":482},{"class":331,"line":341},[483],{"type":45,"tag":329,"props":484,"children":485},{},[486],{"type":51,"value":487},"class UserSerializer(serializers.ModelSerializer):\n",{"type":45,"tag":329,"props":489,"children":490},{"class":331,"line":350},[491],{"type":45,"tag":329,"props":492,"children":493},{},[494],{"type":51,"value":495},"    order_count = serializers.SerializerMethodField()\n",{"type":45,"tag":329,"props":497,"children":498},{"class":331,"line":359},[499],{"type":45,"tag":329,"props":500,"children":501},{"emptyLinePlaceholder":372},[502],{"type":51,"value":375},{"type":45,"tag":329,"props":504,"children":505},{"class":331,"line":368},[506],{"type":45,"tag":329,"props":507,"children":508},{},[509],{"type":51,"value":510},"    def get_order_count(self, obj):\n",{"type":45,"tag":329,"props":512,"children":513},{"class":331,"line":378},[514],{"type":45,"tag":329,"props":515,"children":516},{},[517],{"type":51,"value":518},"        return obj.orders.count()  # ← query per user\n",{"type":45,"tag":329,"props":520,"children":521},{"class":331,"line":387},[522],{"type":45,"tag":329,"props":523,"children":524},{"emptyLinePlaceholder":372},[525],{"type":51,"value":375},{"type":45,"tag":329,"props":527,"children":528},{"class":331,"line":396},[529],{"type":45,"tag":329,"props":530,"children":531},{},[532],{"type":51,"value":533},"# SOLUTION: Annotate in viewset, access in serializer\n",{"type":45,"tag":329,"props":535,"children":536},{"class":331,"line":405},[537],{"type":45,"tag":329,"props":538,"children":539},{},[540],{"type":51,"value":541},"class UserViewSet(viewsets.ModelViewSet):\n",{"type":45,"tag":329,"props":543,"children":544},{"class":331,"line":414},[545],{"type":45,"tag":329,"props":546,"children":547},{},[548],{"type":51,"value":549},"    def get_queryset(self):\n",{"type":45,"tag":329,"props":551,"children":552},{"class":331,"line":422},[553],{"type":45,"tag":329,"props":554,"children":555},{},[556],{"type":51,"value":557},"        return User.objects.annotate(order_count=Count('orders'))\n",{"type":45,"tag":329,"props":559,"children":560},{"class":331,"line":431},[561],{"type":45,"tag":329,"props":562,"children":563},{"emptyLinePlaceholder":372},[564],{"type":51,"value":375},{"type":45,"tag":329,"props":566,"children":567},{"class":331,"line":439},[568],{"type":45,"tag":329,"props":569,"children":570},{},[571],{"type":51,"value":487},{"type":45,"tag":329,"props":573,"children":574},{"class":331,"line":448},[575],{"type":45,"tag":329,"props":576,"children":577},{},[578],{"type":51,"value":579},"    order_count = serializers.IntegerField(read_only=True)\n",{"type":45,"tag":306,"props":581,"children":583},{"id":582},"rule-model-properties-that-query-are-dangerous-in-loops",[584],{"type":51,"value":585},"Rule: Model properties that query are dangerous in loops",{"type":45,"tag":318,"props":587,"children":589},{"className":320,"code":588,"language":322,"meta":323,"style":323},"# PROBLEM: Property triggers query when accessed\nclass User(models.Model):\n    @property\n    def recent_orders(self):\n        return self.orders.filter(created__gte=last_week)[:5]\n\n# Used in template loop = N+1\n\n# SOLUTION: Use Prefetch with custom queryset, or annotate\n",[590],{"type":45,"tag":297,"props":591,"children":592},{"__ignoreMap":323},[593,601,609,617,625,633,640,648,655],{"type":45,"tag":329,"props":594,"children":595},{"class":331,"line":332},[596],{"type":45,"tag":329,"props":597,"children":598},{},[599],{"type":51,"value":600},"# PROBLEM: Property triggers query when accessed\n",{"type":45,"tag":329,"props":602,"children":603},{"class":331,"line":341},[604],{"type":45,"tag":329,"props":605,"children":606},{},[607],{"type":51,"value":608},"class User(models.Model):\n",{"type":45,"tag":329,"props":610,"children":611},{"class":331,"line":350},[612],{"type":45,"tag":329,"props":613,"children":614},{},[615],{"type":51,"value":616},"    @property\n",{"type":45,"tag":329,"props":618,"children":619},{"class":331,"line":359},[620],{"type":45,"tag":329,"props":621,"children":622},{},[623],{"type":51,"value":624},"    def recent_orders(self):\n",{"type":45,"tag":329,"props":626,"children":627},{"class":331,"line":368},[628],{"type":45,"tag":329,"props":629,"children":630},{},[631],{"type":51,"value":632},"        return self.orders.filter(created__gte=last_week)[:5]\n",{"type":45,"tag":329,"props":634,"children":635},{"class":331,"line":378},[636],{"type":45,"tag":329,"props":637,"children":638},{"emptyLinePlaceholder":372},[639],{"type":51,"value":375},{"type":45,"tag":329,"props":641,"children":642},{"class":331,"line":387},[643],{"type":45,"tag":329,"props":644,"children":645},{},[646],{"type":51,"value":647},"# Used in template loop = N+1\n",{"type":45,"tag":329,"props":649,"children":650},{"class":331,"line":396},[651],{"type":45,"tag":329,"props":652,"children":653},{"emptyLinePlaceholder":372},[654],{"type":51,"value":375},{"type":45,"tag":329,"props":656,"children":657},{"class":331,"line":405},[658],{"type":45,"tag":329,"props":659,"children":660},{},[661],{"type":51,"value":662},"# SOLUTION: Use Prefetch with custom queryset, or annotate\n",{"type":45,"tag":306,"props":664,"children":666},{"id":665},"validation-checklist-for-n1",[667],{"type":51,"value":668},"Validation Checklist for N+1",{"type":45,"tag":670,"props":671,"children":674},"ul",{"className":672},[673],"contains-task-list",[675,687,696,705,714],{"type":45,"tag":79,"props":676,"children":679},{"className":677},[678],"task-list-item",[680,685],{"type":45,"tag":681,"props":682,"children":684},"input",{"disabled":372,"type":683},"checkbox",[],{"type":51,"value":686}," Traced data flow from view to template\u002Fserializer",{"type":45,"tag":79,"props":688,"children":690},{"className":689},[678],[691,694],{"type":45,"tag":681,"props":692,"children":693},{"disabled":372,"type":683},[],{"type":51,"value":695}," Confirmed related field is accessed inside a loop",{"type":45,"tag":79,"props":697,"children":699},{"className":698},[678],[700,703],{"type":45,"tag":681,"props":701,"children":702},{"disabled":372,"type":683},[],{"type":51,"value":704}," Searched codebase for existing select_related\u002Fprefetch_related",{"type":45,"tag":79,"props":706,"children":708},{"className":707},[678],[709,712],{"type":45,"tag":681,"props":710,"children":711},{"disabled":372,"type":683},[],{"type":51,"value":713}," Verified table has significant row count (1000+)",{"type":45,"tag":79,"props":715,"children":717},{"className":716},[678],[718,721],{"type":45,"tag":681,"props":719,"children":720},{"disabled":372,"type":683},[],{"type":51,"value":722}," Confirmed this is a hot path (not admin, not rare action)",{"type":45,"tag":277,"props":724,"children":725},{},[],{"type":45,"tag":68,"props":727,"children":729},{"id":728},"priority-2-unbounded-querysets-critical",[730],{"type":51,"value":731},"Priority 2: Unbounded Querysets (CRITICAL)",{"type":45,"tag":54,"props":733,"children":734},{},[735,739],{"type":45,"tag":60,"props":736,"children":737},{},[738],{"type":51,"value":293},{"type":51,"value":740}," Loading entire tables exhausts memory. Large tables cause OOM kills and worker restarts.",{"type":45,"tag":306,"props":742,"children":744},{"id":743},"rule-always-paginate-list-endpoints",[745],{"type":51,"value":746},"Rule: Always paginate list endpoints",{"type":45,"tag":318,"props":748,"children":750},{"className":320,"code":749,"language":322,"meta":323,"style":323},"# PROBLEM: No pagination - loads all rows\nclass UserListView(ListView):\n    model = User\n    template_name = 'users.html'\n\n# SOLUTION: Add pagination\nclass UserListView(ListView):\n    model = User\n    template_name = 'users.html'\n    paginate_by = 25\n",[751],{"type":45,"tag":297,"props":752,"children":753},{"__ignoreMap":323},[754,762,770,778,786,793,801,808,815,822],{"type":45,"tag":329,"props":755,"children":756},{"class":331,"line":332},[757],{"type":45,"tag":329,"props":758,"children":759},{},[760],{"type":51,"value":761},"# PROBLEM: No pagination - loads all rows\n",{"type":45,"tag":329,"props":763,"children":764},{"class":331,"line":341},[765],{"type":45,"tag":329,"props":766,"children":767},{},[768],{"type":51,"value":769},"class UserListView(ListView):\n",{"type":45,"tag":329,"props":771,"children":772},{"class":331,"line":350},[773],{"type":45,"tag":329,"props":774,"children":775},{},[776],{"type":51,"value":777},"    model = User\n",{"type":45,"tag":329,"props":779,"children":780},{"class":331,"line":359},[781],{"type":45,"tag":329,"props":782,"children":783},{},[784],{"type":51,"value":785},"    template_name = 'users.html'\n",{"type":45,"tag":329,"props":787,"children":788},{"class":331,"line":368},[789],{"type":45,"tag":329,"props":790,"children":791},{"emptyLinePlaceholder":372},[792],{"type":51,"value":375},{"type":45,"tag":329,"props":794,"children":795},{"class":331,"line":378},[796],{"type":45,"tag":329,"props":797,"children":798},{},[799],{"type":51,"value":800},"# SOLUTION: Add pagination\n",{"type":45,"tag":329,"props":802,"children":803},{"class":331,"line":387},[804],{"type":45,"tag":329,"props":805,"children":806},{},[807],{"type":51,"value":769},{"type":45,"tag":329,"props":809,"children":810},{"class":331,"line":396},[811],{"type":45,"tag":329,"props":812,"children":813},{},[814],{"type":51,"value":777},{"type":45,"tag":329,"props":816,"children":817},{"class":331,"line":405},[818],{"type":45,"tag":329,"props":819,"children":820},{},[821],{"type":51,"value":785},{"type":45,"tag":329,"props":823,"children":824},{"class":331,"line":414},[825],{"type":45,"tag":329,"props":826,"children":827},{},[828],{"type":51,"value":829},"    paginate_by = 25\n",{"type":45,"tag":306,"props":831,"children":833},{"id":832},"rule-use-iterator-for-large-batch-processing",[834],{"type":51,"value":835},"Rule: Use iterator() for large batch processing",{"type":45,"tag":318,"props":837,"children":839},{"className":320,"code":838,"language":322,"meta":323,"style":323},"# PROBLEM: Loads all objects into memory at once\nfor user in User.objects.all():\n    process(user)\n\n# SOLUTION: Stream with iterator()\nfor user in User.objects.iterator(chunk_size=1000):\n    process(user)\n",[840],{"type":45,"tag":297,"props":841,"children":842},{"__ignoreMap":323},[843,851,859,867,874,882,890],{"type":45,"tag":329,"props":844,"children":845},{"class":331,"line":332},[846],{"type":45,"tag":329,"props":847,"children":848},{},[849],{"type":51,"value":850},"# PROBLEM: Loads all objects into memory at once\n",{"type":45,"tag":329,"props":852,"children":853},{"class":331,"line":341},[854],{"type":45,"tag":329,"props":855,"children":856},{},[857],{"type":51,"value":858},"for user in User.objects.all():\n",{"type":45,"tag":329,"props":860,"children":861},{"class":331,"line":350},[862],{"type":45,"tag":329,"props":863,"children":864},{},[865],{"type":51,"value":866},"    process(user)\n",{"type":45,"tag":329,"props":868,"children":869},{"class":331,"line":359},[870],{"type":45,"tag":329,"props":871,"children":872},{"emptyLinePlaceholder":372},[873],{"type":51,"value":375},{"type":45,"tag":329,"props":875,"children":876},{"class":331,"line":368},[877],{"type":45,"tag":329,"props":878,"children":879},{},[880],{"type":51,"value":881},"# SOLUTION: Stream with iterator()\n",{"type":45,"tag":329,"props":883,"children":884},{"class":331,"line":378},[885],{"type":45,"tag":329,"props":886,"children":887},{},[888],{"type":51,"value":889},"for user in User.objects.iterator(chunk_size=1000):\n",{"type":45,"tag":329,"props":891,"children":892},{"class":331,"line":387},[893],{"type":45,"tag":329,"props":894,"children":895},{},[896],{"type":51,"value":866},{"type":45,"tag":306,"props":898,"children":900},{"id":899},"rule-never-call-list-on-unbounded-querysets",[901],{"type":51,"value":902},"Rule: Never call list() on unbounded querysets",{"type":45,"tag":318,"props":904,"children":906},{"className":320,"code":905,"language":322,"meta":323,"style":323},"# PROBLEM: Forces full evaluation into memory\nall_users = list(User.objects.all())\n\n# SOLUTION: Keep as queryset, slice if needed\nusers = User.objects.all()[:100]\n",[907],{"type":45,"tag":297,"props":908,"children":909},{"__ignoreMap":323},[910,918,926,933,941],{"type":45,"tag":329,"props":911,"children":912},{"class":331,"line":332},[913],{"type":45,"tag":329,"props":914,"children":915},{},[916],{"type":51,"value":917},"# PROBLEM: Forces full evaluation into memory\n",{"type":45,"tag":329,"props":919,"children":920},{"class":331,"line":341},[921],{"type":45,"tag":329,"props":922,"children":923},{},[924],{"type":51,"value":925},"all_users = list(User.objects.all())\n",{"type":45,"tag":329,"props":927,"children":928},{"class":331,"line":350},[929],{"type":45,"tag":329,"props":930,"children":931},{"emptyLinePlaceholder":372},[932],{"type":51,"value":375},{"type":45,"tag":329,"props":934,"children":935},{"class":331,"line":359},[936],{"type":45,"tag":329,"props":937,"children":938},{},[939],{"type":51,"value":940},"# SOLUTION: Keep as queryset, slice if needed\n",{"type":45,"tag":329,"props":942,"children":943},{"class":331,"line":368},[944],{"type":45,"tag":329,"props":945,"children":946},{},[947],{"type":51,"value":948},"users = User.objects.all()[:100]\n",{"type":45,"tag":306,"props":950,"children":952},{"id":951},"validation-checklist-for-unbounded-querysets",[953],{"type":51,"value":954},"Validation Checklist for Unbounded Querysets",{"type":45,"tag":670,"props":956,"children":958},{"className":957},[673],[959,968,977],{"type":45,"tag":79,"props":960,"children":962},{"className":961},[678],[963,966],{"type":45,"tag":681,"props":964,"children":965},{"disabled":372,"type":683},[],{"type":51,"value":967}," Table is large (10k+ rows) or will grow unbounded",{"type":45,"tag":79,"props":969,"children":971},{"className":970},[678],[972,975],{"type":45,"tag":681,"props":973,"children":974},{"disabled":372,"type":683},[],{"type":51,"value":976}," No pagination class, paginate_by, or slicing",{"type":45,"tag":79,"props":978,"children":980},{"className":979},[678],[981,984],{"type":45,"tag":681,"props":982,"children":983},{"disabled":372,"type":683},[],{"type":51,"value":985}," This runs on user-facing request (not background job with chunking)",{"type":45,"tag":277,"props":987,"children":988},{},[],{"type":45,"tag":68,"props":990,"children":992},{"id":991},"priority-3-missing-indexes-high",[993],{"type":51,"value":994},"Priority 3: Missing Indexes (HIGH)",{"type":45,"tag":54,"props":996,"children":997},{},[998,1002],{"type":45,"tag":60,"props":999,"children":1000},{},[1001],{"type":51,"value":293},{"type":51,"value":1003}," Full table scans. Negligible on small tables, catastrophic on large ones.",{"type":45,"tag":306,"props":1005,"children":1007},{"id":1006},"rule-index-fields-used-in-where-clauses-on-large-tables",[1008],{"type":51,"value":1009},"Rule: Index fields used in WHERE clauses on large tables",{"type":45,"tag":318,"props":1011,"children":1013},{"className":320,"code":1012,"language":322,"meta":323,"style":323},"# PROBLEM: Filtering on unindexed field\n# User.objects.filter(email=email)  # full scan if no index\n\nclass User(models.Model):\n    email = models.EmailField()  # ← no db_index\n\n# SOLUTION: Add index\nclass User(models.Model):\n    email = models.EmailField(db_index=True)\n",[1014],{"type":45,"tag":297,"props":1015,"children":1016},{"__ignoreMap":323},[1017,1025,1033,1040,1047,1055,1062,1070,1077],{"type":45,"tag":329,"props":1018,"children":1019},{"class":331,"line":332},[1020],{"type":45,"tag":329,"props":1021,"children":1022},{},[1023],{"type":51,"value":1024},"# PROBLEM: Filtering on unindexed field\n",{"type":45,"tag":329,"props":1026,"children":1027},{"class":331,"line":341},[1028],{"type":45,"tag":329,"props":1029,"children":1030},{},[1031],{"type":51,"value":1032},"# User.objects.filter(email=email)  # full scan if no index\n",{"type":45,"tag":329,"props":1034,"children":1035},{"class":331,"line":350},[1036],{"type":45,"tag":329,"props":1037,"children":1038},{"emptyLinePlaceholder":372},[1039],{"type":51,"value":375},{"type":45,"tag":329,"props":1041,"children":1042},{"class":331,"line":359},[1043],{"type":45,"tag":329,"props":1044,"children":1045},{},[1046],{"type":51,"value":608},{"type":45,"tag":329,"props":1048,"children":1049},{"class":331,"line":368},[1050],{"type":45,"tag":329,"props":1051,"children":1052},{},[1053],{"type":51,"value":1054},"    email = models.EmailField()  # ← no db_index\n",{"type":45,"tag":329,"props":1056,"children":1057},{"class":331,"line":378},[1058],{"type":45,"tag":329,"props":1059,"children":1060},{"emptyLinePlaceholder":372},[1061],{"type":51,"value":375},{"type":45,"tag":329,"props":1063,"children":1064},{"class":331,"line":387},[1065],{"type":45,"tag":329,"props":1066,"children":1067},{},[1068],{"type":51,"value":1069},"# SOLUTION: Add index\n",{"type":45,"tag":329,"props":1071,"children":1072},{"class":331,"line":396},[1073],{"type":45,"tag":329,"props":1074,"children":1075},{},[1076],{"type":51,"value":608},{"type":45,"tag":329,"props":1078,"children":1079},{"class":331,"line":405},[1080],{"type":45,"tag":329,"props":1081,"children":1082},{},[1083],{"type":51,"value":1084},"    email = models.EmailField(db_index=True)\n",{"type":45,"tag":306,"props":1086,"children":1088},{"id":1087},"rule-index-fields-used-in-order-by-on-large-tables",[1089],{"type":51,"value":1090},"Rule: Index fields used in ORDER BY on large tables",{"type":45,"tag":318,"props":1092,"children":1094},{"className":320,"code":1093,"language":322,"meta":323,"style":323},"# PROBLEM: Sorting requires full scan without index\nOrder.objects.order_by('-created')\n\n# SOLUTION: Index the sort field\nclass Order(models.Model):\n    created = models.DateTimeField(db_index=True)\n",[1095],{"type":45,"tag":297,"props":1096,"children":1097},{"__ignoreMap":323},[1098,1106,1114,1121,1129,1137],{"type":45,"tag":329,"props":1099,"children":1100},{"class":331,"line":332},[1101],{"type":45,"tag":329,"props":1102,"children":1103},{},[1104],{"type":51,"value":1105},"# PROBLEM: Sorting requires full scan without index\n",{"type":45,"tag":329,"props":1107,"children":1108},{"class":331,"line":341},[1109],{"type":45,"tag":329,"props":1110,"children":1111},{},[1112],{"type":51,"value":1113},"Order.objects.order_by('-created')\n",{"type":45,"tag":329,"props":1115,"children":1116},{"class":331,"line":350},[1117],{"type":45,"tag":329,"props":1118,"children":1119},{"emptyLinePlaceholder":372},[1120],{"type":51,"value":375},{"type":45,"tag":329,"props":1122,"children":1123},{"class":331,"line":359},[1124],{"type":45,"tag":329,"props":1125,"children":1126},{},[1127],{"type":51,"value":1128},"# SOLUTION: Index the sort field\n",{"type":45,"tag":329,"props":1130,"children":1131},{"class":331,"line":368},[1132],{"type":45,"tag":329,"props":1133,"children":1134},{},[1135],{"type":51,"value":1136},"class Order(models.Model):\n",{"type":45,"tag":329,"props":1138,"children":1139},{"class":331,"line":378},[1140],{"type":45,"tag":329,"props":1141,"children":1142},{},[1143],{"type":51,"value":1144},"    created = models.DateTimeField(db_index=True)\n",{"type":45,"tag":306,"props":1146,"children":1148},{"id":1147},"rule-use-composite-indexes-for-common-query-patterns",[1149],{"type":51,"value":1150},"Rule: Use composite indexes for common query patterns",{"type":45,"tag":318,"props":1152,"children":1154},{"className":320,"code":1153,"language":322,"meta":323,"style":323},"class Order(models.Model):\n    user = models.ForeignKey(User)\n    status = models.CharField(max_length=20)\n    created = models.DateTimeField()\n\n    class Meta:\n        indexes = [\n            models.Index(fields=['user', 'status']),  # for filter(user=x, status=y)\n            models.Index(fields=['status', '-created']),  # for filter(status=x).order_by('-created')\n        ]\n",[1155],{"type":45,"tag":297,"props":1156,"children":1157},{"__ignoreMap":323},[1158,1165,1173,1181,1189,1196,1204,1212,1220,1228],{"type":45,"tag":329,"props":1159,"children":1160},{"class":331,"line":332},[1161],{"type":45,"tag":329,"props":1162,"children":1163},{},[1164],{"type":51,"value":1136},{"type":45,"tag":329,"props":1166,"children":1167},{"class":331,"line":341},[1168],{"type":45,"tag":329,"props":1169,"children":1170},{},[1171],{"type":51,"value":1172},"    user = models.ForeignKey(User)\n",{"type":45,"tag":329,"props":1174,"children":1175},{"class":331,"line":350},[1176],{"type":45,"tag":329,"props":1177,"children":1178},{},[1179],{"type":51,"value":1180},"    status = models.CharField(max_length=20)\n",{"type":45,"tag":329,"props":1182,"children":1183},{"class":331,"line":359},[1184],{"type":45,"tag":329,"props":1185,"children":1186},{},[1187],{"type":51,"value":1188},"    created = models.DateTimeField()\n",{"type":45,"tag":329,"props":1190,"children":1191},{"class":331,"line":368},[1192],{"type":45,"tag":329,"props":1193,"children":1194},{"emptyLinePlaceholder":372},[1195],{"type":51,"value":375},{"type":45,"tag":329,"props":1197,"children":1198},{"class":331,"line":378},[1199],{"type":45,"tag":329,"props":1200,"children":1201},{},[1202],{"type":51,"value":1203},"    class Meta:\n",{"type":45,"tag":329,"props":1205,"children":1206},{"class":331,"line":387},[1207],{"type":45,"tag":329,"props":1208,"children":1209},{},[1210],{"type":51,"value":1211},"        indexes = [\n",{"type":45,"tag":329,"props":1213,"children":1214},{"class":331,"line":396},[1215],{"type":45,"tag":329,"props":1216,"children":1217},{},[1218],{"type":51,"value":1219},"            models.Index(fields=['user', 'status']),  # for filter(user=x, status=y)\n",{"type":45,"tag":329,"props":1221,"children":1222},{"class":331,"line":405},[1223],{"type":45,"tag":329,"props":1224,"children":1225},{},[1226],{"type":51,"value":1227},"            models.Index(fields=['status', '-created']),  # for filter(status=x).order_by('-created')\n",{"type":45,"tag":329,"props":1229,"children":1230},{"class":331,"line":414},[1231],{"type":45,"tag":329,"props":1232,"children":1233},{},[1234],{"type":51,"value":1235},"        ]\n",{"type":45,"tag":306,"props":1237,"children":1239},{"id":1238},"validation-checklist-for-missing-indexes",[1240],{"type":51,"value":1241},"Validation Checklist for Missing Indexes",{"type":45,"tag":670,"props":1243,"children":1245},{"className":1244},[673],[1246,1255,1264,1273],{"type":45,"tag":79,"props":1247,"children":1249},{"className":1248},[678],[1250,1253],{"type":45,"tag":681,"props":1251,"children":1252},{"disabled":372,"type":683},[],{"type":51,"value":1254}," Table has 10k+ rows",{"type":45,"tag":79,"props":1256,"children":1258},{"className":1257},[678],[1259,1262],{"type":45,"tag":681,"props":1260,"children":1261},{"disabled":372,"type":683},[],{"type":51,"value":1263}," Field is used in filter() or order_by() on hot path",{"type":45,"tag":79,"props":1265,"children":1267},{"className":1266},[678],[1268,1271],{"type":45,"tag":681,"props":1269,"children":1270},{"disabled":372,"type":683},[],{"type":51,"value":1272}," Checked model - no db_index=True or Meta.indexes entry",{"type":45,"tag":79,"props":1274,"children":1276},{"className":1275},[678],[1277,1280],{"type":45,"tag":681,"props":1278,"children":1279},{"disabled":372,"type":683},[],{"type":51,"value":1281}," Not a foreign key (already indexed automatically)",{"type":45,"tag":277,"props":1283,"children":1284},{},[],{"type":45,"tag":68,"props":1286,"children":1288},{"id":1287},"priority-4-write-loops-high",[1289],{"type":51,"value":1290},"Priority 4: Write Loops (HIGH)",{"type":45,"tag":54,"props":1292,"children":1293},{},[1294,1298],{"type":45,"tag":60,"props":1295,"children":1296},{},[1297],{"type":51,"value":293},{"type":51,"value":1299}," N database writes instead of 1. Lock contention. Slow requests.",{"type":45,"tag":306,"props":1301,"children":1303},{"id":1302},"rule-use-bulk_create-instead-of-create-in-loops",[1304],{"type":51,"value":1305},"Rule: Use bulk_create instead of create() in loops",{"type":45,"tag":318,"props":1307,"children":1309},{"className":320,"code":1308,"language":322,"meta":323,"style":323},"# PROBLEM: N inserts, N round trips\nfor item in items:\n    Model.objects.create(name=item['name'])\n\n# SOLUTION: Single bulk insert\nModel.objects.bulk_create([\n    Model(name=item['name']) for item in items\n])\n",[1310],{"type":45,"tag":297,"props":1311,"children":1312},{"__ignoreMap":323},[1313,1321,1329,1337,1344,1352,1360,1368],{"type":45,"tag":329,"props":1314,"children":1315},{"class":331,"line":332},[1316],{"type":45,"tag":329,"props":1317,"children":1318},{},[1319],{"type":51,"value":1320},"# PROBLEM: N inserts, N round trips\n",{"type":45,"tag":329,"props":1322,"children":1323},{"class":331,"line":341},[1324],{"type":45,"tag":329,"props":1325,"children":1326},{},[1327],{"type":51,"value":1328},"for item in items:\n",{"type":45,"tag":329,"props":1330,"children":1331},{"class":331,"line":350},[1332],{"type":45,"tag":329,"props":1333,"children":1334},{},[1335],{"type":51,"value":1336},"    Model.objects.create(name=item['name'])\n",{"type":45,"tag":329,"props":1338,"children":1339},{"class":331,"line":359},[1340],{"type":45,"tag":329,"props":1341,"children":1342},{"emptyLinePlaceholder":372},[1343],{"type":51,"value":375},{"type":45,"tag":329,"props":1345,"children":1346},{"class":331,"line":368},[1347],{"type":45,"tag":329,"props":1348,"children":1349},{},[1350],{"type":51,"value":1351},"# SOLUTION: Single bulk insert\n",{"type":45,"tag":329,"props":1353,"children":1354},{"class":331,"line":378},[1355],{"type":45,"tag":329,"props":1356,"children":1357},{},[1358],{"type":51,"value":1359},"Model.objects.bulk_create([\n",{"type":45,"tag":329,"props":1361,"children":1362},{"class":331,"line":387},[1363],{"type":45,"tag":329,"props":1364,"children":1365},{},[1366],{"type":51,"value":1367},"    Model(name=item['name']) for item in items\n",{"type":45,"tag":329,"props":1369,"children":1370},{"class":331,"line":396},[1371],{"type":45,"tag":329,"props":1372,"children":1373},{},[1374],{"type":51,"value":1375},"])\n",{"type":45,"tag":306,"props":1377,"children":1379},{"id":1378},"rule-use-update-or-bulk_update-instead-of-save-in-loops",[1380],{"type":51,"value":1381},"Rule: Use update() or bulk_update instead of save() in loops",{"type":45,"tag":318,"props":1383,"children":1385},{"className":320,"code":1384,"language":322,"meta":323,"style":323},"# PROBLEM: N updates\nfor obj in queryset:\n    obj.status = 'done'\n    obj.save()\n\n# SOLUTION A: Single UPDATE statement (same value for all)\nqueryset.update(status='done')\n\n# SOLUTION B: bulk_update (different values)\nfor obj in objects:\n    obj.status = compute_status(obj)\nModel.objects.bulk_update(objects, ['status'], batch_size=500)\n",[1386],{"type":45,"tag":297,"props":1387,"children":1388},{"__ignoreMap":323},[1389,1397,1405,1413,1421,1428,1436,1444,1451,1459,1467,1475],{"type":45,"tag":329,"props":1390,"children":1391},{"class":331,"line":332},[1392],{"type":45,"tag":329,"props":1393,"children":1394},{},[1395],{"type":51,"value":1396},"# PROBLEM: N updates\n",{"type":45,"tag":329,"props":1398,"children":1399},{"class":331,"line":341},[1400],{"type":45,"tag":329,"props":1401,"children":1402},{},[1403],{"type":51,"value":1404},"for obj in queryset:\n",{"type":45,"tag":329,"props":1406,"children":1407},{"class":331,"line":350},[1408],{"type":45,"tag":329,"props":1409,"children":1410},{},[1411],{"type":51,"value":1412},"    obj.status = 'done'\n",{"type":45,"tag":329,"props":1414,"children":1415},{"class":331,"line":359},[1416],{"type":45,"tag":329,"props":1417,"children":1418},{},[1419],{"type":51,"value":1420},"    obj.save()\n",{"type":45,"tag":329,"props":1422,"children":1423},{"class":331,"line":368},[1424],{"type":45,"tag":329,"props":1425,"children":1426},{"emptyLinePlaceholder":372},[1427],{"type":51,"value":375},{"type":45,"tag":329,"props":1429,"children":1430},{"class":331,"line":378},[1431],{"type":45,"tag":329,"props":1432,"children":1433},{},[1434],{"type":51,"value":1435},"# SOLUTION A: Single UPDATE statement (same value for all)\n",{"type":45,"tag":329,"props":1437,"children":1438},{"class":331,"line":387},[1439],{"type":45,"tag":329,"props":1440,"children":1441},{},[1442],{"type":51,"value":1443},"queryset.update(status='done')\n",{"type":45,"tag":329,"props":1445,"children":1446},{"class":331,"line":396},[1447],{"type":45,"tag":329,"props":1448,"children":1449},{"emptyLinePlaceholder":372},[1450],{"type":51,"value":375},{"type":45,"tag":329,"props":1452,"children":1453},{"class":331,"line":405},[1454],{"type":45,"tag":329,"props":1455,"children":1456},{},[1457],{"type":51,"value":1458},"# SOLUTION B: bulk_update (different values)\n",{"type":45,"tag":329,"props":1460,"children":1461},{"class":331,"line":414},[1462],{"type":45,"tag":329,"props":1463,"children":1464},{},[1465],{"type":51,"value":1466},"for obj in objects:\n",{"type":45,"tag":329,"props":1468,"children":1469},{"class":331,"line":422},[1470],{"type":45,"tag":329,"props":1471,"children":1472},{},[1473],{"type":51,"value":1474},"    obj.status = compute_status(obj)\n",{"type":45,"tag":329,"props":1476,"children":1477},{"class":331,"line":431},[1478],{"type":45,"tag":329,"props":1479,"children":1480},{},[1481],{"type":51,"value":1482},"Model.objects.bulk_update(objects, ['status'], batch_size=500)\n",{"type":45,"tag":306,"props":1484,"children":1486},{"id":1485},"rule-use-delete-on-queryset-not-in-loops",[1487],{"type":51,"value":1488},"Rule: Use delete() on queryset, not in loops",{"type":45,"tag":318,"props":1490,"children":1492},{"className":320,"code":1491,"language":322,"meta":323,"style":323},"# PROBLEM: N deletes\nfor obj in queryset:\n    obj.delete()\n\n# SOLUTION: Single DELETE\nqueryset.delete()\n",[1493],{"type":45,"tag":297,"props":1494,"children":1495},{"__ignoreMap":323},[1496,1504,1511,1519,1526,1534],{"type":45,"tag":329,"props":1497,"children":1498},{"class":331,"line":332},[1499],{"type":45,"tag":329,"props":1500,"children":1501},{},[1502],{"type":51,"value":1503},"# PROBLEM: N deletes\n",{"type":45,"tag":329,"props":1505,"children":1506},{"class":331,"line":341},[1507],{"type":45,"tag":329,"props":1508,"children":1509},{},[1510],{"type":51,"value":1404},{"type":45,"tag":329,"props":1512,"children":1513},{"class":331,"line":350},[1514],{"type":45,"tag":329,"props":1515,"children":1516},{},[1517],{"type":51,"value":1518},"    obj.delete()\n",{"type":45,"tag":329,"props":1520,"children":1521},{"class":331,"line":359},[1522],{"type":45,"tag":329,"props":1523,"children":1524},{"emptyLinePlaceholder":372},[1525],{"type":51,"value":375},{"type":45,"tag":329,"props":1527,"children":1528},{"class":331,"line":368},[1529],{"type":45,"tag":329,"props":1530,"children":1531},{},[1532],{"type":51,"value":1533},"# SOLUTION: Single DELETE\n",{"type":45,"tag":329,"props":1535,"children":1536},{"class":331,"line":378},[1537],{"type":45,"tag":329,"props":1538,"children":1539},{},[1540],{"type":51,"value":1541},"queryset.delete()\n",{"type":45,"tag":306,"props":1543,"children":1545},{"id":1544},"validation-checklist-for-write-loops",[1546],{"type":51,"value":1547},"Validation Checklist for Write Loops",{"type":45,"tag":670,"props":1549,"children":1551},{"className":1550},[673],[1552,1561,1570],{"type":45,"tag":79,"props":1553,"children":1555},{"className":1554},[678],[1556,1559],{"type":45,"tag":681,"props":1557,"children":1558},{"disabled":372,"type":683},[],{"type":51,"value":1560}," Loop iterates over 100+ items (or unbounded)",{"type":45,"tag":79,"props":1562,"children":1564},{"className":1563},[678],[1565,1568],{"type":45,"tag":681,"props":1566,"children":1567},{"disabled":372,"type":683},[],{"type":51,"value":1569}," Each iteration calls create(), save(), or delete()",{"type":45,"tag":79,"props":1571,"children":1573},{"className":1572},[678],[1574,1577],{"type":45,"tag":681,"props":1575,"children":1576},{"disabled":372,"type":683},[],{"type":51,"value":1578}," This runs on user-facing request (not one-time migration script)",{"type":45,"tag":277,"props":1580,"children":1581},{},[],{"type":45,"tag":68,"props":1583,"children":1585},{"id":1584},"priority-5-inefficient-patterns-low",[1586],{"type":51,"value":1587},"Priority 5: Inefficient Patterns (LOW)",{"type":45,"tag":54,"props":1589,"children":1590},{},[1591,1596],{"type":45,"tag":60,"props":1592,"children":1593},{},[1594],{"type":51,"value":1595},"Rarely worth reporting.",{"type":51,"value":1597}," Include only as minor notes if you're already reporting real issues.",{"type":45,"tag":306,"props":1599,"children":1601},{"id":1600},"pattern-count-vs-exists",[1602],{"type":51,"value":1603},"Pattern: count() vs exists()",{"type":45,"tag":318,"props":1605,"children":1607},{"className":320,"code":1606,"language":322,"meta":323,"style":323},"# Slightly suboptimal\nif queryset.count() > 0:\n    do_thing()\n\n# Marginally better\nif queryset.exists():\n    do_thing()\n",[1608],{"type":45,"tag":297,"props":1609,"children":1610},{"__ignoreMap":323},[1611,1619,1627,1635,1642,1650,1658],{"type":45,"tag":329,"props":1612,"children":1613},{"class":331,"line":332},[1614],{"type":45,"tag":329,"props":1615,"children":1616},{},[1617],{"type":51,"value":1618},"# Slightly suboptimal\n",{"type":45,"tag":329,"props":1620,"children":1621},{"class":331,"line":341},[1622],{"type":45,"tag":329,"props":1623,"children":1624},{},[1625],{"type":51,"value":1626},"if queryset.count() > 0:\n",{"type":45,"tag":329,"props":1628,"children":1629},{"class":331,"line":350},[1630],{"type":45,"tag":329,"props":1631,"children":1632},{},[1633],{"type":51,"value":1634},"    do_thing()\n",{"type":45,"tag":329,"props":1636,"children":1637},{"class":331,"line":359},[1638],{"type":45,"tag":329,"props":1639,"children":1640},{"emptyLinePlaceholder":372},[1641],{"type":51,"value":375},{"type":45,"tag":329,"props":1643,"children":1644},{"class":331,"line":368},[1645],{"type":45,"tag":329,"props":1646,"children":1647},{},[1648],{"type":51,"value":1649},"# Marginally better\n",{"type":45,"tag":329,"props":1651,"children":1652},{"class":331,"line":378},[1653],{"type":45,"tag":329,"props":1654,"children":1655},{},[1656],{"type":51,"value":1657},"if queryset.exists():\n",{"type":45,"tag":329,"props":1659,"children":1660},{"class":331,"line":387},[1661],{"type":45,"tag":329,"props":1662,"children":1663},{},[1664],{"type":51,"value":1634},{"type":45,"tag":54,"props":1666,"children":1667},{},[1668,1673],{"type":45,"tag":60,"props":1669,"children":1670},{},[1671],{"type":51,"value":1672},"Usually skip",{"type":51,"value":1674}," - difference is \u003C1ms in most cases.",{"type":45,"tag":306,"props":1676,"children":1678},{"id":1677},"pattern-lenqueryset-vs-count",[1679],{"type":51,"value":1680},"Pattern: len(queryset) vs count()",{"type":45,"tag":318,"props":1682,"children":1684},{"className":320,"code":1683,"language":322,"meta":323,"style":323},"# Fetches all rows to count\nif len(queryset) > 0:  # bad if queryset not yet evaluated\n\n# Single COUNT query\nif queryset.count() > 0:\n",[1685],{"type":45,"tag":297,"props":1686,"children":1687},{"__ignoreMap":323},[1688,1696,1704,1711,1719],{"type":45,"tag":329,"props":1689,"children":1690},{"class":331,"line":332},[1691],{"type":45,"tag":329,"props":1692,"children":1693},{},[1694],{"type":51,"value":1695},"# Fetches all rows to count\n",{"type":45,"tag":329,"props":1697,"children":1698},{"class":331,"line":341},[1699],{"type":45,"tag":329,"props":1700,"children":1701},{},[1702],{"type":51,"value":1703},"if len(queryset) > 0:  # bad if queryset not yet evaluated\n",{"type":45,"tag":329,"props":1705,"children":1706},{"class":331,"line":350},[1707],{"type":45,"tag":329,"props":1708,"children":1709},{"emptyLinePlaceholder":372},[1710],{"type":51,"value":375},{"type":45,"tag":329,"props":1712,"children":1713},{"class":331,"line":359},[1714],{"type":45,"tag":329,"props":1715,"children":1716},{},[1717],{"type":51,"value":1718},"# Single COUNT query\n",{"type":45,"tag":329,"props":1720,"children":1721},{"class":331,"line":368},[1722],{"type":45,"tag":329,"props":1723,"children":1724},{},[1725],{"type":51,"value":1626},{"type":45,"tag":54,"props":1727,"children":1728},{},[1729,1734],{"type":45,"tag":60,"props":1730,"children":1731},{},[1732],{"type":51,"value":1733},"Only flag",{"type":51,"value":1735}," if queryset is large and not already evaluated.",{"type":45,"tag":306,"props":1737,"children":1739},{"id":1738},"pattern-get-in-small-loops",[1740],{"type":51,"value":1741},"Pattern: get() in small loops",{"type":45,"tag":318,"props":1743,"children":1745},{"className":320,"code":1744,"language":322,"meta":323,"style":323},"# N queries, but if N is small (\u003C 20), often fine\nfor id in ids:\n    obj = Model.objects.get(id=id)\n",[1746],{"type":45,"tag":297,"props":1747,"children":1748},{"__ignoreMap":323},[1749,1757,1765],{"type":45,"tag":329,"props":1750,"children":1751},{"class":331,"line":332},[1752],{"type":45,"tag":329,"props":1753,"children":1754},{},[1755],{"type":51,"value":1756},"# N queries, but if N is small (\u003C 20), often fine\n",{"type":45,"tag":329,"props":1758,"children":1759},{"class":331,"line":341},[1760],{"type":45,"tag":329,"props":1761,"children":1762},{},[1763],{"type":51,"value":1764},"for id in ids:\n",{"type":45,"tag":329,"props":1766,"children":1767},{"class":331,"line":350},[1768],{"type":45,"tag":329,"props":1769,"children":1770},{},[1771],{"type":51,"value":1772},"    obj = Model.objects.get(id=id)\n",{"type":45,"tag":54,"props":1774,"children":1775},{},[1776,1780],{"type":45,"tag":60,"props":1777,"children":1778},{},[1779],{"type":51,"value":1733},{"type":51,"value":1781}," if loop is large or this is in a very hot path.",{"type":45,"tag":277,"props":1783,"children":1784},{},[],{"type":45,"tag":68,"props":1786,"children":1788},{"id":1787},"validation-requirements",[1789],{"type":51,"value":1790},"Validation Requirements",{"type":45,"tag":54,"props":1792,"children":1793},{},[1794],{"type":51,"value":1795},"Before reporting ANY issue:",{"type":45,"tag":75,"props":1797,"children":1798},{},[1799,1809,1819,1829,1839],{"type":45,"tag":79,"props":1800,"children":1801},{},[1802,1807],{"type":45,"tag":60,"props":1803,"children":1804},{},[1805],{"type":51,"value":1806},"Trace the data flow",{"type":51,"value":1808}," - Follow queryset from creation to consumption",{"type":45,"tag":79,"props":1810,"children":1811},{},[1812,1817],{"type":45,"tag":60,"props":1813,"children":1814},{},[1815],{"type":51,"value":1816},"Search for existing optimizations",{"type":51,"value":1818}," - Grep for select_related, prefetch_related, pagination",{"type":45,"tag":79,"props":1820,"children":1821},{},[1822,1827],{"type":45,"tag":60,"props":1823,"children":1824},{},[1825],{"type":51,"value":1826},"Verify data volume",{"type":51,"value":1828}," - Check if table is actually large",{"type":45,"tag":79,"props":1830,"children":1831},{},[1832,1837],{"type":45,"tag":60,"props":1833,"children":1834},{},[1835],{"type":51,"value":1836},"Confirm hot path",{"type":51,"value":1838}," - Trace call sites, verify this runs frequently",{"type":45,"tag":79,"props":1840,"children":1841},{},[1842,1847],{"type":45,"tag":60,"props":1843,"children":1844},{},[1845],{"type":51,"value":1846},"Rule out mitigations",{"type":51,"value":1848}," - Check for caching, rate limiting",{"type":45,"tag":54,"props":1850,"children":1851},{},[1852],{"type":45,"tag":60,"props":1853,"children":1854},{},[1855],{"type":51,"value":1856},"If you cannot validate all steps, do not report.",{"type":45,"tag":277,"props":1858,"children":1859},{},[],{"type":45,"tag":68,"props":1861,"children":1863},{"id":1862},"output-format",[1864],{"type":51,"value":1865},"Output Format",{"type":45,"tag":318,"props":1867,"children":1871},{"className":1868,"code":1869,"language":1870,"meta":323,"style":323},"language-markdown shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","## Django Performance Review: [File\u002FComponent Name]\n\n### Summary\nValidated issues: X (Y Critical, Z High)\n\n### Findings\n\n#### [PERF-001] N+1 Query in UserListView (CRITICAL)\n**Location:** `views.py:45`\n\n**Issue:** Related field `profile` accessed in template loop without prefetch.\n\n**Validation:**\n- Traced: UserListView → users queryset → user_list.html → `{{ user.profile.bio }}` in loop\n- Searched codebase: no select_related('profile') found\n- User table: 50k+ rows (verified in admin)\n- Hot path: linked from homepage navigation\n\n**Evidence:**\n```python\ndef get_queryset(self):\n    return User.objects.filter(active=True)  # no select_related\n","markdown",[1872],{"type":45,"tag":297,"props":1873,"children":1874},{"__ignoreMap":323},[1875,1890,1897,1910,1919,1926,1938,1945,1969,2003,2010,2050,2057,2074,2105,2118,2131,2144,2152,2169,2184,2193],{"type":45,"tag":329,"props":1876,"children":1877},{"class":331,"line":332},[1878,1884],{"type":45,"tag":329,"props":1879,"children":1881},{"style":1880},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1882],{"type":51,"value":1883},"## ",{"type":45,"tag":329,"props":1885,"children":1887},{"style":1886},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1888],{"type":51,"value":1889},"Django Performance Review: [File\u002FComponent Name]\n",{"type":45,"tag":329,"props":1891,"children":1892},{"class":331,"line":341},[1893],{"type":45,"tag":329,"props":1894,"children":1895},{"emptyLinePlaceholder":372},[1896],{"type":51,"value":375},{"type":45,"tag":329,"props":1898,"children":1899},{"class":331,"line":350},[1900,1905],{"type":45,"tag":329,"props":1901,"children":1902},{"style":1880},[1903],{"type":51,"value":1904},"### ",{"type":45,"tag":329,"props":1906,"children":1907},{"style":1886},[1908],{"type":51,"value":1909},"Summary\n",{"type":45,"tag":329,"props":1911,"children":1912},{"class":331,"line":359},[1913],{"type":45,"tag":329,"props":1914,"children":1916},{"style":1915},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1917],{"type":51,"value":1918},"Validated issues: X (Y Critical, Z High)\n",{"type":45,"tag":329,"props":1920,"children":1921},{"class":331,"line":368},[1922],{"type":45,"tag":329,"props":1923,"children":1924},{"emptyLinePlaceholder":372},[1925],{"type":51,"value":375},{"type":45,"tag":329,"props":1927,"children":1928},{"class":331,"line":378},[1929,1933],{"type":45,"tag":329,"props":1930,"children":1931},{"style":1880},[1932],{"type":51,"value":1904},{"type":45,"tag":329,"props":1934,"children":1935},{"style":1886},[1936],{"type":51,"value":1937},"Findings\n",{"type":45,"tag":329,"props":1939,"children":1940},{"class":331,"line":387},[1941],{"type":45,"tag":329,"props":1942,"children":1943},{"emptyLinePlaceholder":372},[1944],{"type":51,"value":375},{"type":45,"tag":329,"props":1946,"children":1947},{"class":331,"line":396},[1948,1953,1959,1964],{"type":45,"tag":329,"props":1949,"children":1950},{"style":1880},[1951],{"type":51,"value":1952},"#### [",{"type":45,"tag":329,"props":1954,"children":1956},{"style":1955},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1957],{"type":51,"value":1958},"PERF-001",{"type":45,"tag":329,"props":1960,"children":1961},{"style":1880},[1962],{"type":51,"value":1963},"]",{"type":45,"tag":329,"props":1965,"children":1966},{"style":1886},[1967],{"type":51,"value":1968}," N+1 Query in UserListView (CRITICAL)\n",{"type":45,"tag":329,"props":1970,"children":1971},{"class":331,"line":405},[1972,1978,1984,1988,1993,1998],{"type":45,"tag":329,"props":1973,"children":1975},{"style":1974},"--shiki-light:#39ADB5;--shiki-light-font-weight:bold;--shiki-default:#89DDFF;--shiki-default-font-weight:bold;--shiki-dark:#89DDFF;--shiki-dark-font-weight:bold",[1976],{"type":51,"value":1977},"**",{"type":45,"tag":329,"props":1979,"children":1981},{"style":1980},"--shiki-light:#E53935;--shiki-light-font-weight:bold;--shiki-default:#F07178;--shiki-default-font-weight:bold;--shiki-dark:#F07178;--shiki-dark-font-weight:bold",[1982],{"type":51,"value":1983},"Location:",{"type":45,"tag":329,"props":1985,"children":1986},{"style":1974},[1987],{"type":51,"value":1977},{"type":45,"tag":329,"props":1989,"children":1990},{"style":1880},[1991],{"type":51,"value":1992}," `",{"type":45,"tag":329,"props":1994,"children":1995},{"style":1955},[1996],{"type":51,"value":1997},"views.py:45",{"type":45,"tag":329,"props":1999,"children":2000},{"style":1880},[2001],{"type":51,"value":2002},"`\n",{"type":45,"tag":329,"props":2004,"children":2005},{"class":331,"line":414},[2006],{"type":45,"tag":329,"props":2007,"children":2008},{"emptyLinePlaceholder":372},[2009],{"type":51,"value":375},{"type":45,"tag":329,"props":2011,"children":2012},{"class":331,"line":422},[2013,2017,2022,2026,2031,2036,2041,2045],{"type":45,"tag":329,"props":2014,"children":2015},{"style":1974},[2016],{"type":51,"value":1977},{"type":45,"tag":329,"props":2018,"children":2019},{"style":1980},[2020],{"type":51,"value":2021},"Issue:",{"type":45,"tag":329,"props":2023,"children":2024},{"style":1974},[2025],{"type":51,"value":1977},{"type":45,"tag":329,"props":2027,"children":2028},{"style":1915},[2029],{"type":51,"value":2030}," Related field ",{"type":45,"tag":329,"props":2032,"children":2033},{"style":1880},[2034],{"type":51,"value":2035},"`",{"type":45,"tag":329,"props":2037,"children":2038},{"style":1955},[2039],{"type":51,"value":2040},"profile",{"type":45,"tag":329,"props":2042,"children":2043},{"style":1880},[2044],{"type":51,"value":2035},{"type":45,"tag":329,"props":2046,"children":2047},{"style":1915},[2048],{"type":51,"value":2049}," accessed in template loop without prefetch.\n",{"type":45,"tag":329,"props":2051,"children":2052},{"class":331,"line":431},[2053],{"type":45,"tag":329,"props":2054,"children":2055},{"emptyLinePlaceholder":372},[2056],{"type":51,"value":375},{"type":45,"tag":329,"props":2058,"children":2059},{"class":331,"line":439},[2060,2064,2069],{"type":45,"tag":329,"props":2061,"children":2062},{"style":1974},[2063],{"type":51,"value":1977},{"type":45,"tag":329,"props":2065,"children":2066},{"style":1980},[2067],{"type":51,"value":2068},"Validation:",{"type":45,"tag":329,"props":2070,"children":2071},{"style":1974},[2072],{"type":51,"value":2073},"**\n",{"type":45,"tag":329,"props":2075,"children":2076},{"class":331,"line":448},[2077,2082,2087,2091,2096,2100],{"type":45,"tag":329,"props":2078,"children":2079},{"style":1880},[2080],{"type":51,"value":2081},"-",{"type":45,"tag":329,"props":2083,"children":2084},{"style":1915},[2085],{"type":51,"value":2086}," Traced: UserListView → users queryset → user_list.html → ",{"type":45,"tag":329,"props":2088,"children":2089},{"style":1880},[2090],{"type":51,"value":2035},{"type":45,"tag":329,"props":2092,"children":2093},{"style":1955},[2094],{"type":51,"value":2095},"{{ user.profile.bio }}",{"type":45,"tag":329,"props":2097,"children":2098},{"style":1880},[2099],{"type":51,"value":2035},{"type":45,"tag":329,"props":2101,"children":2102},{"style":1915},[2103],{"type":51,"value":2104}," in loop\n",{"type":45,"tag":329,"props":2106,"children":2108},{"class":331,"line":2107},15,[2109,2113],{"type":45,"tag":329,"props":2110,"children":2111},{"style":1880},[2112],{"type":51,"value":2081},{"type":45,"tag":329,"props":2114,"children":2115},{"style":1915},[2116],{"type":51,"value":2117}," Searched codebase: no select_related('profile') found\n",{"type":45,"tag":329,"props":2119,"children":2121},{"class":331,"line":2120},16,[2122,2126],{"type":45,"tag":329,"props":2123,"children":2124},{"style":1880},[2125],{"type":51,"value":2081},{"type":45,"tag":329,"props":2127,"children":2128},{"style":1915},[2129],{"type":51,"value":2130}," User table: 50k+ rows (verified in admin)\n",{"type":45,"tag":329,"props":2132,"children":2134},{"class":331,"line":2133},17,[2135,2139],{"type":45,"tag":329,"props":2136,"children":2137},{"style":1880},[2138],{"type":51,"value":2081},{"type":45,"tag":329,"props":2140,"children":2141},{"style":1915},[2142],{"type":51,"value":2143}," Hot path: linked from homepage navigation\n",{"type":45,"tag":329,"props":2145,"children":2147},{"class":331,"line":2146},18,[2148],{"type":45,"tag":329,"props":2149,"children":2150},{"emptyLinePlaceholder":372},[2151],{"type":51,"value":375},{"type":45,"tag":329,"props":2153,"children":2155},{"class":331,"line":2154},19,[2156,2160,2165],{"type":45,"tag":329,"props":2157,"children":2158},{"style":1974},[2159],{"type":51,"value":1977},{"type":45,"tag":329,"props":2161,"children":2162},{"style":1980},[2163],{"type":51,"value":2164},"Evidence:",{"type":45,"tag":329,"props":2166,"children":2167},{"style":1974},[2168],{"type":51,"value":2073},{"type":45,"tag":329,"props":2170,"children":2172},{"class":331,"line":2171},20,[2173,2178],{"type":45,"tag":329,"props":2174,"children":2175},{"style":1955},[2176],{"type":51,"value":2177},"```",{"type":45,"tag":329,"props":2179,"children":2181},{"style":2180},"--shiki-light:#90A4AE90;--shiki-default:#EEFFFF90;--shiki-dark:#BABED890",[2182],{"type":51,"value":2183},"python\n",{"type":45,"tag":329,"props":2185,"children":2187},{"class":331,"line":2186},21,[2188],{"type":45,"tag":329,"props":2189,"children":2190},{"style":2180},[2191],{"type":51,"value":2192},"def get_queryset(self):\n",{"type":45,"tag":329,"props":2194,"children":2196},{"class":331,"line":2195},22,[2197],{"type":45,"tag":329,"props":2198,"children":2199},{"style":2180},[2200],{"type":51,"value":2201},"    return User.objects.filter(active=True)  # no select_related\n",{"type":45,"tag":54,"props":2203,"children":2204},{},[2205],{"type":45,"tag":60,"props":2206,"children":2207},{},[2208],{"type":51,"value":2209},"Fix:",{"type":45,"tag":318,"props":2211,"children":2213},{"className":320,"code":2212,"language":322,"meta":323,"style":323},"def get_queryset(self):\n    return User.objects.filter(active=True).select_related('profile')\n",[2214],{"type":45,"tag":297,"props":2215,"children":2216},{"__ignoreMap":323},[2217,2224],{"type":45,"tag":329,"props":2218,"children":2219},{"class":331,"line":332},[2220],{"type":45,"tag":329,"props":2221,"children":2222},{},[2223],{"type":51,"value":2192},{"type":45,"tag":329,"props":2225,"children":2226},{"class":331,"line":341},[2227],{"type":45,"tag":329,"props":2228,"children":2229},{},[2230],{"type":51,"value":2231},"    return User.objects.filter(active=True).select_related('profile')\n",{"type":45,"tag":318,"props":2233,"children":2237},{"className":2234,"code":2236,"language":51},[2235],"language-text","\nIf no issues found: \"No performance issues identified after reviewing [files] and validating [what you checked].\"\n\n**Before submitting, sanity check each finding:**\n- Does the severity match the actual impact? (\"Minor inefficiency\" ≠ CRITICAL)\n- Is this a real performance issue or just a style preference?\n- Would fixing this measurably improve performance?\n\nIf the answer to any is \"no\" - remove the finding.\n\n---\n\n## What NOT to Report\n\n- Test files\n- Admin-only views\n- Management commands\n- Migration files\n- One-time scripts\n- Code behind disabled feature flags\n- Tables with \u003C1000 rows that won't grow\n- Patterns in cold paths (rarely executed code)\n- Micro-optimizations (exists vs count, only\u002Fdefer without evidence)\n\n### False Positives to Avoid\n\n**Queryset variable assignment is not an issue:**\n```python\n# This is FINE - no performance difference\nprojects_qs = Project.objects.filter(org=org)\nprojects = list(projects_qs)\n\n# vs this - identical performance\nprojects = list(Project.objects.filter(org=org))\n",[2238],{"type":45,"tag":297,"props":2239,"children":2240},{"__ignoreMap":323},[2241],{"type":51,"value":2236},{"type":45,"tag":54,"props":2243,"children":2244},{},[2245],{"type":51,"value":2246},"Querysets are lazy. Assigning to a variable doesn't execute anything.",{"type":45,"tag":54,"props":2248,"children":2249},{},[2250],{"type":45,"tag":60,"props":2251,"children":2252},{},[2253],{"type":51,"value":2254},"Single query patterns are not N+1:",{"type":45,"tag":318,"props":2256,"children":2258},{"className":320,"code":2257,"language":322,"meta":323,"style":323},"# This is ONE query, not N+1\nprojects = list(Project.objects.filter(org=org))\n",[2259],{"type":45,"tag":297,"props":2260,"children":2261},{"__ignoreMap":323},[2262,2270],{"type":45,"tag":329,"props":2263,"children":2264},{"class":331,"line":332},[2265],{"type":45,"tag":329,"props":2266,"children":2267},{},[2268],{"type":51,"value":2269},"# This is ONE query, not N+1\n",{"type":45,"tag":329,"props":2271,"children":2272},{"class":331,"line":341},[2273],{"type":45,"tag":329,"props":2274,"children":2275},{},[2276],{"type":51,"value":2277},"projects = list(Project.objects.filter(org=org))\n",{"type":45,"tag":54,"props":2279,"children":2280},{},[2281,2283,2289],{"type":51,"value":2282},"N+1 requires a loop that triggers additional queries. A single ",{"type":45,"tag":297,"props":2284,"children":2286},{"className":2285},[],[2287],{"type":51,"value":2288},"list()",{"type":51,"value":2290}," call is fine.",{"type":45,"tag":54,"props":2292,"children":2293},{},[2294],{"type":45,"tag":60,"props":2295,"children":2296},{},[2297],{"type":51,"value":2298},"Missing select_related on single object fetch is not N+1:",{"type":45,"tag":318,"props":2300,"children":2302},{"className":320,"code":2301,"language":322,"meta":323,"style":323},"# This is 2 queries, not N+1 - report as LOW at most\nstate = AutofixState.objects.filter(pr_id=pr_id).first()\nproject_id = state.request.project_id  # second query\n",[2303],{"type":45,"tag":297,"props":2304,"children":2305},{"__ignoreMap":323},[2306,2314,2322],{"type":45,"tag":329,"props":2307,"children":2308},{"class":331,"line":332},[2309],{"type":45,"tag":329,"props":2310,"children":2311},{},[2312],{"type":51,"value":2313},"# This is 2 queries, not N+1 - report as LOW at most\n",{"type":45,"tag":329,"props":2315,"children":2316},{"class":331,"line":341},[2317],{"type":45,"tag":329,"props":2318,"children":2319},{},[2320],{"type":51,"value":2321},"state = AutofixState.objects.filter(pr_id=pr_id).first()\n",{"type":45,"tag":329,"props":2323,"children":2324},{"class":331,"line":350},[2325],{"type":45,"tag":329,"props":2326,"children":2327},{},[2328],{"type":51,"value":2329},"project_id = state.request.project_id  # second query\n",{"type":45,"tag":54,"props":2331,"children":2332},{},[2333],{"type":51,"value":2334},"N+1 requires a loop. A single object doing 2 queries instead of 1 can be reported as LOW if relevant, but never as CRITICAL\u002FHIGH.",{"type":45,"tag":54,"props":2336,"children":2337},{},[2338,2343],{"type":45,"tag":60,"props":2339,"children":2340},{},[2341],{"type":51,"value":2342},"Style preferences are not performance issues:",{"type":51,"value":2344},"\nIf your only suggestion is \"combine these two lines\" or \"rename this variable\" - that's style, not performance. Don't report it.",{"type":45,"tag":2346,"props":2347,"children":2348},"style",{},[2349],{"type":51,"value":2350},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2352,"total":2515},[2353,2378,2392,2405,2419,2436,2452,2462,2472,2483,2493,2508],{"slug":2354,"name":2354,"fn":2355,"description":2356,"org":2357,"tags":2358,"stars":2375,"repoUrl":2376,"updatedAt":2377},"xcodebuildmcp","build and test Apple apps with XcodeBuildMCP","Official skill for XcodeBuildMCP. Use when doing iOS\u002FmacOS\u002FwatchOS\u002FtvOS\u002FvisionOS work (build, test, run, debug, log, UI automation).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2359,2362,2365,2368,2369,2372],{"name":2360,"slug":2361,"type":16},"Debugging","debugging",{"name":2363,"slug":2364,"type":16},"iOS","ios",{"name":2366,"slug":2367,"type":16},"macOS","macos",{"name":9,"slug":8,"type":16},{"name":2370,"slug":2371,"type":16},"Testing","testing",{"name":2373,"slug":2374,"type":16},"Xcode","xcode",6176,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002FXcodeBuildMCP","2026-04-06T18:13:34.8719",{"slug":2379,"name":2379,"fn":2380,"description":2381,"org":2382,"tags":2383,"stars":2375,"repoUrl":2376,"updatedAt":2391},"xcodebuildmcp-cli","build and test Apple apps via CLI","Official skill for the XcodeBuildMCP CLI. Use when doing iOS\u002FmacOS\u002FwatchOS\u002FtvOS\u002FvisionOS work (build, test, run, debug, log, UI automation).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2384,2387,2388,2389,2390],{"name":2385,"slug":2386,"type":16},"CLI","cli",{"name":2363,"slug":2364,"type":16},{"name":2366,"slug":2367,"type":16},{"name":2370,"slug":2371,"type":16},{"name":2373,"slug":2374,"type":16},"2026-04-06T18:13:36.13414",{"slug":2393,"name":2393,"fn":2394,"description":2395,"org":2396,"tags":2397,"stars":26,"repoUrl":27,"updatedAt":2404},"agents-md","maintain project instruction files","Creates and maintains concise AGENTS.md and CLAUDE.md project instruction files. Use when asked to create AGENTS.md, update AGENTS.md, maintain agent docs, set up CLAUDE.md, document repository agent conventions, or keep coding-agent instructions minimal and reference-backed.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2398,2401],{"name":2399,"slug":2400,"type":16},"Documentation","documentation",{"name":2402,"slug":2403,"type":16},"Engineering","engineering","2026-05-15T06:16:29.695991",{"slug":2406,"name":2406,"fn":2407,"description":2408,"org":2409,"tags":2410,"stars":26,"repoUrl":27,"updatedAt":2418},"blog-writing-guide","write and review engineering blog posts","Write, review, and improve blog posts for the Sentry engineering blog following Sentry's specific writing standards, voice, and quality bar. Use this skill whenever someone asks to write a blog post, draft a technical article, review blog content, improve a draft, write a product announcement, create an engineering deep-dive, or produce any written content destined for the Sentry blog or developer audience. Also trigger when the user mentions \"blog post,\" \"blog draft,\" \"write-up,\" \"announcement post,\" \"engineering post,\" \"deep dive,\" \"postmortem,\" or asks for help with technical writing for Sentry. Even if the user just says \"help me write about [feature\u002Ftopic]\" — if it sounds like it could become a Sentry blog post, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2411,2414,2415],{"name":2412,"slug":2413,"type":16},"Communications","communications",{"name":9,"slug":8,"type":16},{"name":2416,"slug":2417,"type":16},"Technical Writing","technical-writing","2026-05-15T06:16:33.38217",{"slug":2420,"name":2420,"fn":2421,"description":2422,"org":2423,"tags":2424,"stars":26,"repoUrl":27,"updatedAt":2435},"brand-guidelines","write copy following Sentry brand guidelines","Write copy following Sentry brand guidelines. Use when writing UI text, error messages, empty states, onboarding flows, 404 pages, documentation, marketing copy, or any user-facing content. Covers both Plain Speech (default) and Sentry Voice tones.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2425,2428,2431,2432],{"name":2426,"slug":2427,"type":16},"Branding","branding",{"name":2429,"slug":2430,"type":16},"Content Creation","content-creation",{"name":9,"slug":8,"type":16},{"name":2433,"slug":2434,"type":16},"UX Copy","ux-copy","2026-05-15T06:16:22.395707",{"slug":2437,"name":2437,"fn":2438,"description":2439,"org":2440,"tags":2441,"stars":26,"repoUrl":27,"updatedAt":2451},"claude-settings-audit","generate Claude Code settings permissions","Analyze a repository to generate recommended Claude Code settings.json permissions. Use when setting up a new project, auditing existing settings, or determining which read-only bash commands to allow. Detects tech stack, build tools, and monorepo structure.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2442,2445,2448],{"name":2443,"slug":2444,"type":16},"Claude Code","claude-code",{"name":2446,"slug":2447,"type":16},"Configuration","configuration",{"name":2449,"slug":2450,"type":16},"Security","security","2026-05-15T06:16:44.335977",{"slug":19,"name":19,"fn":2453,"description":2454,"org":2455,"tags":2456,"stars":26,"repoUrl":27,"updatedAt":2461},"perform code reviews for Sentry projects","Perform code reviews following Sentry engineering practices. Use when reviewing pull requests, examining code changes, or providing feedback on code quality. Covers security, performance, testing, and design review.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2457,2458,2459,2460],{"name":18,"slug":19,"type":16},{"name":2402,"slug":2403,"type":16},{"name":14,"slug":15,"type":16},{"name":2449,"slug":2450,"type":16},"2026-05-15T06:16:35.824864",{"slug":2463,"name":2463,"fn":2464,"description":2465,"org":2466,"tags":2467,"stars":26,"repoUrl":27,"updatedAt":2471},"code-simplifier","simplify and refine source code","Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality. Use when asked to \"simplify code\", \"clean up code\", \"refactor for clarity\", \"improve readability\", or review recently modified code for elegance. Focuses on project-specific best practices.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2468],{"name":2469,"slug":2470,"type":16},"Code Analysis","code-analysis","2026-05-15T06:16:32.127981",{"slug":2473,"name":2473,"fn":2474,"description":2475,"org":2476,"tags":2477,"stars":26,"repoUrl":27,"updatedAt":2482},"commit","create commits with Sentry conventions","Use for every request to commit changes or draft a commit message. Creates Sentry-style conventional commits with issue references.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2478,2481],{"name":2479,"slug":2480,"type":16},"Git","git",{"name":9,"slug":8,"type":16},"2026-07-18T05:15:10.723937",{"slug":2484,"name":2484,"fn":2485,"description":2486,"org":2487,"tags":2488,"stars":26,"repoUrl":27,"updatedAt":2492},"create-branch","create git branches for Sentry workflows","Create a git branch following Sentry naming conventions. Use when asked to \"create a branch\", \"new branch\", \"start a branch\", \"make a branch\", \"switch to a new branch\", or when starting new work on the default branch.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2489,2490,2491],{"name":2402,"slug":2403,"type":16},{"name":2479,"slug":2480,"type":16},{"name":9,"slug":8,"type":16},"2026-05-15T06:16:39.458431",{"slug":2494,"name":2494,"fn":2495,"description":2496,"org":2497,"tags":2498,"stars":26,"repoUrl":27,"updatedAt":2507},"django-access-review","review Django access control and IDOR","Django access control and IDOR security review. Use when reviewing Django views, DRF viewsets, ORM queries, or any Python\u002FDjango code handling user authorization. Trigger keywords: \"IDOR\", \"access control\", \"authorization\", \"Django permissions\", \"object permissions\", \"tenant isolation\", \"broken access\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2499,2502,2503,2504,2506],{"name":2500,"slug":2501,"type":16},"Access Control","access-control",{"name":2469,"slug":2470,"type":16},{"name":24,"slug":25,"type":16},{"name":2505,"slug":322,"type":16},"Python",{"name":2449,"slug":2450,"type":16},"2026-05-15T06:16:43.098698",{"slug":4,"name":4,"fn":5,"description":6,"org":2509,"tags":2510,"stars":26,"repoUrl":27,"updatedAt":28},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2511,2512,2513,2514],{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16},{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},88,{"items":2517,"total":2558},[2518,2523,2529,2536,2542,2549,2553],{"slug":2393,"name":2393,"fn":2394,"description":2395,"org":2519,"tags":2520,"stars":26,"repoUrl":27,"updatedAt":2404},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2521,2522],{"name":2399,"slug":2400,"type":16},{"name":2402,"slug":2403,"type":16},{"slug":2406,"name":2406,"fn":2407,"description":2408,"org":2524,"tags":2525,"stars":26,"repoUrl":27,"updatedAt":2418},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2526,2527,2528],{"name":2412,"slug":2413,"type":16},{"name":9,"slug":8,"type":16},{"name":2416,"slug":2417,"type":16},{"slug":2420,"name":2420,"fn":2421,"description":2422,"org":2530,"tags":2531,"stars":26,"repoUrl":27,"updatedAt":2435},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2532,2533,2534,2535],{"name":2426,"slug":2427,"type":16},{"name":2429,"slug":2430,"type":16},{"name":9,"slug":8,"type":16},{"name":2433,"slug":2434,"type":16},{"slug":2437,"name":2437,"fn":2438,"description":2439,"org":2537,"tags":2538,"stars":26,"repoUrl":27,"updatedAt":2451},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2539,2540,2541],{"name":2443,"slug":2444,"type":16},{"name":2446,"slug":2447,"type":16},{"name":2449,"slug":2450,"type":16},{"slug":19,"name":19,"fn":2453,"description":2454,"org":2543,"tags":2544,"stars":26,"repoUrl":27,"updatedAt":2461},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2545,2546,2547,2548],{"name":18,"slug":19,"type":16},{"name":2402,"slug":2403,"type":16},{"name":14,"slug":15,"type":16},{"name":2449,"slug":2450,"type":16},{"slug":2463,"name":2463,"fn":2464,"description":2465,"org":2550,"tags":2551,"stars":26,"repoUrl":27,"updatedAt":2471},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2552],{"name":2469,"slug":2470,"type":16},{"slug":2473,"name":2473,"fn":2474,"description":2475,"org":2554,"tags":2555,"stars":26,"repoUrl":27,"updatedAt":2482},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2556,2557],{"name":2479,"slug":2480,"type":16},{"name":9,"slug":8,"type":16},28]