برنامه نویسی

مدیریت کلان داده با Django و AgGrid

ما قصد داریم اپلیکیشنی بسازیم که بتواند حجم زیادی از داده ها را با AgGrid فیلتر و مرتب کند.

ارسال تمام داده های خود به مرورگر یک راه سریع برای شروع با AgGrid است، اما اگر یک میلیون ردیف داشته باشید چه؟ آن وقت است که فیلترینگ و مرتب سازی سمت سرور به کمک می آید.

این راهنما شامل:

  • استفاده از AgGrid در حالت اسکرول بی نهایت به طوری که سرور مسئول صفحه بندی، فیلتر کردن و مرتب سازی است.
  • پیاده سازی فیلتر، مرتب سازی و صفحه بندی برای AgGrid در بالای کلاس Django ListView
  • ایجاد یک فرمان مدیریت جنگو برای پر کردن داده های آزمایشی

اما ابتدا، در اینجا نگاهی اجمالی به آنچه که امروز در حال ساخت آن هستیم می پردازیم:

توضیحات تصویر

پیش نمایش پروژه

آیا می خواهید نسخه زنده برنامه را ببینید؟ اکنون می توانید یک کپی از این پروژه ایجاد کرده و آن را امتحان کنید.

یک فورک از پروژه AgGridDemo در Circumeo ایجاد کنید.

راه اندازی برنامه جنگو

بسته ها را نصب کنید و برنامه جنگو را ایجاد کنید.

pip install --upgrade django faker
django-admin startproject bigdata .
python3 manage.py startapp core
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هسته را به INSTALLED_APPS فهرست

# settings.py
INSTALLED_APPS = [
  "core",
    ...
]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اضافه کردن قالب ها

  • یک دایرکتوری به نام ایجاد کنید templates در داخل core برنامه
  • یک فایل به نام ایجاد کنید index.html در داخل templates فهرست راهنما.
<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/ag-grid/dist/ag-grid.min.noStyle.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/ag-grid/dist/styles/ag-grid.css">
    <link rel="stylesheet" href="https://unpkg.com/ag-grid/dist/styles/ag-theme-balham.css">
    <style>
      #container {
        max-width: 1250px;
        width: 100%;
        height: 100%;
        margin: 0px auto;
        margin-top: 30px;
      }
    </style>
  </head>
  <body>
    <div id="container">
      <div id="data-grid" style="height: 600px; width: 100%" class="ag-theme-balham"></div>
    </div>
    <script type="text/javascript" charset="utf-8">
      const numberFilterParams = {
        filterParams: {
          filterOptions: ["equals", "notEqual", "lessThan", "greaterThan"],
          suppressAndOrCondition: true,
        },
      };

      var columnDefs = [
        { headerName: "Name", field: "name", filter: "agTextColumnFilter" },
        {
          headerName: "Description",
          field: "description",
          filter: "agTextColumnFilter",
        },
        { headerName: "Category", field: "category", filter: "agTextColumnFilter" },
        {
          headerName: "Price",
          field: "price",
          filter: "agNumberColumnFilter",
          ...numberFilterParams,
        },
        {
          headerName: "Stock Quantity",
          field: "stock_quantity",
          filter: "agNumberColumnFilter",
          ...numberFilterParams,
        },
        {
          headerName: "Rating",
          field: "rating",
          filter: "agNumberColumnFilter",
          ...numberFilterParams,
        },
      ];

      var gridOptions = {
        columnDefs: columnDefs,
        defaultColDef: {
          filterParams: {
            suppressAndOrCondition: true,
          },
        },
        enableServerSideSorting: true,
        enableServerSideFilter: true,
        rowModelType: "infinite",
        cacheBlockSize: 100,
        maxBlocksInCache: 10,
      };

      var dataSource = {
        getRows: function (params) {
          var filtering = encodeURIComponent(JSON.stringify(params.filterModel));
          var sorting = encodeURIComponent(JSON.stringify(params.sortModel));

          var startRow = params.startRow;
          var endRow = params.endRow;

          var url = `/products?startRow=${startRow}&endRow=${endRow}&filter=${filtering}&sort=${sorting}`;

          fetch(url)
            .then((response) => response.json())
            .then((data) => {
            params.successCallback(data.rows, data.totalRows);
          })
            .catch((err) => {
            params.failCallback();
          });
        },
      };

      var gridDiv = document.querySelector("#data-grid");

      new agGrid.Grid(gridDiv, gridOptions);
      gridOptions.api.setDatasource(dataSource);
      gridOptions.api.sizeColumnsToFit();
    </script>
  </body>
</html>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن نماها

  • موجود را حذف کنید views.py فایل.
  • یک دایرکتوری جدید با نام ایجاد کنید views در داخل core پوشه
  • یک فایل جدید با نام ایجاد کنید index.py در داخل views فهرست راهنما.
  • موارد زیر را کپی و در آن پیست کنید index.py در داخل views فهرست راهنما.
from django.shortcuts import render

def index_view(request):
    return render(request, "core/index.html")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • فایل دیگری با نام ایجاد کنید products.py در همان دایرکتوری
  • موارد زیر را کپی و در قسمت پیست کنید products.py فایل.
import json

from django.views.generic.list import ListView
from django.http import JsonResponse
from django.db.models import Q

from core.models import Product


class ProductListView(ListView):
    model = Product

    def get_queryset(self):
        """
        Convert AgGrid filter and sort objects into a Django query.
        An example filter:
            {"category": {"type": "contains", "filter": "electronics"}}
        """
        queryset = super().get_queryset()

        filter_params = self.request.GET.get("filter", None)
        if filter_params:
            filters = json.loads(filter_params)
            q_objects = Q()

            for key, filter_info in filters.items():
                filter_type = filter_info.get("type")
                filter_value = filter_info.get("filter")

                if filter_type == "contains":
                    lookup = f"{key}__icontains"
                    q_objects &= Q(**{lookup: filter_value})
                elif filter_type == "equals":
                    lookup = f"{key}__exact"
                    q_objects &= Q(**{lookup: filter_value})
                elif filter_type == "notEqual":
                    lookup = f"{key}__exact"
                    q_objects &= ~Q(**{lookup: filter_value})
                elif filter_type == "greaterThan":
                    lookup = f"{key}__gt"
                    q_objects &= Q(**{lookup: filter_value})
                elif filter_type == "lessThan":
                    lookup = f"{key}__lt"
                    q_objects &= Q(**{lookup: filter_value})

            queryset = queryset.filter(q_objects)

        sort_params = self.request.GET.get("sort", None)
        if sort_params:
            sort_objects = json.loads(sort_params)
            sort_fields = []
            for sort_object in sort_objects:
                col_id = sort_object["colId"]
                sort_order = sort_object["sort"]
                if sort_order == "asc":
                    sort_fields.append(col_id)
                elif sort_order == "desc":
                    sort_fields.append(f"-{col_id}")

            if sort_fields:
                queryset = queryset.order_by(*sort_fields)
        return queryset

    def get(self, request, *args, **kwargs):
        start_row = int(request.GET.get("startRow", 0))
        end_row = int(request.GET.get("endRow", 100))

        queryset = self.get_queryset()

        total_rows = queryset.count()
        queryset = queryset[start_row:end_row]

        products = list(
            queryset.values(
                "name", "description", "category", "price", "stock_quantity", "rating"
            )
        )

        return JsonResponse({"rows": products, "totalRows": total_rows})
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی URL ها

  • urls.py را در فهرست اصلی ایجاد کنید.
from django.urls import path

from core.views.index import index_view
from core.views.products import ProductListView

urlpatterns = [
    path("", index_view, name="index"),
    path("products", ProductListView.as_view(), name="products"),
]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • urls.py موجود را در دایرکتوری bigdata پروژه به روز کنید.
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("core.urls")),
]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اضافه کردن مدل های پایگاه داده

موجود را بازنویسی کنید models.py با موارد زیر:

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField(blank=True)
    category = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock_quantity = models.IntegerField()
    rating = models.FloatField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن یک فرمان مدیریت جنگو برای داده های تست

  • ساختار دایرکتوری را ایجاد کنید management/commands در داخل core پوشه
  • فایلی به نام باز کنید populate_products.py در دایرکتوری جدید و موارد زیر را وارد کنید.
from faker import Faker
import random

from django.core.management.base import BaseCommand
from core.models import Product

class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        faker = Faker()

        categories = [
            'Electronics',
            'Books',
            'Clothing',
            'Home & Garden',
            'Toys & Games',
            'Sports & Outdoors',
            'Health & Beauty',
            'Automotive',
            'Groceries',
            'Pet Supplies'
        ]

        for _ in range(1000):
            Product.objects.create(
                name=faker.text(max_nb_chars=20).capitalize(),
                description=faker.text(),
                category=random.choice(categories),
                price=round(random.uniform(5.0, 500.0), 2),
                stock_quantity=random.randint(0, 100),
                rating=round(random.uniform(1.0, 5.0), 1)
            )

        self.stdout.write(self.style.SUCCESS('Successfully populated the database with products.'))
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اجرای دستور مدیریت جنگو، یک جلسه پوسته باز کنید.

python3 manage.py populate_products
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بالا و در حال اجرا با Django و AgGrid

اکنون آماده هستید که میلیون ها ردیف را بدون خراب کردن برگه های مرورگر مدیریت کنید! فهرست های پایگاه داده را در صورت نیاز اضافه کنید، و می توانید این رویکرد را تا حد زیادی مقیاس کنید.

نوشته های مشابه

دکمه بازگشت به بالا