我该如何修复它,以便该函数不会填满 RAM?

问题描述 投票:0回答:1

每次调用该函数时,都会从Mysql中导出xlsx。但为什么数据会写入 RAM (+ 1GB),并且直到删除对象并运行垃圾收集器后才会释放?您可以以某种方式决定数据库中的数据不写入 RAM,而是写入临时文件。或者也许我的请求代码是错误的,那么你能帮我解决这个问题吗?

@sync_to_async
@login_required
def export_excel(request):
    chars = 'abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    number = int(10)
    length = int(10)
    for n in range(number):
        password = ''
        for i in range(length):
            password += random.choice(chars)
    pricesecret = Price.objects.get(id=1)
    path = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + ""
    path2 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip"
    path3 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/rar"
    path4 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx"
    try:
        os.makedirs(path)
        os.makedirs(path2)
        os.makedirs(path3)
        os.makedirs(path4)
    except:
        path = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + ""
        path2 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip"
        path3 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/rar"
        path4 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx"
    xlsxpathname = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx")
    zippathname = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip")
    if xlsxpathname:
            xlsxpathname = ''.join(map(str, xlsxpathname))
            filepath = '/var/www/django/mysite/static/media/pricenew/' + str(request.user.usrid.listusers_id) + '/xlsx/' + xlsxpathname
    if zippathname:
            zippathname = ''.join(map(str, zippathname))
            filepath4 = '/var/www/django/mysite/static/media/pricenew/' + str(request.user.usrid.listusers_id) + '/zip/' + zippathname
    filepath2 = '/var/www/django/mysite/static/media/pricenew/' + str(request.user.usrid.listusers_id) + '/rar/' + pricesecret.url + '.lock'
    if os.path.exists(filepath2) and os.path.exists(filepath) and os.stat(filepath).st_size != 0 and os.stat(filepath4).st_size != 0:
        url = "https://localhost/media/pricenew/" + str(
            request.user.usrid.listusers_id) + "/zip/" + zippathname
        return JsonResponse({'pricesecret': zippathname, 'url': url,})
    else:
        files = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/rar/")
        files2 = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx/")
        files3 = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip/")
        if files:
            onlyfiles3 = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/rar/")
            onlyfiles3 = ''.join(map(str, onlyfiles3))
            eawa3 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/rar/"
            os.remove(eawa3 + onlyfiles3)
        if files2:
            onlyfiles1 = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx/")
            onlyfiles1 = ''.join(map(str, onlyfiles1))
            eawa = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx/"
            os.remove(eawa + onlyfiles1)
        if files3:
            onlyfiles2 = os.listdir("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip/")
            onlyfiles2 = ''.join(map(str, onlyfiles2))
            eawa2 = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip/"
            os.remove(eawa2 + onlyfiles2)
        file_lock = open(filepath2, "wb")
        price_title = "Прайс_" + datetime.today().strftime('%Y-%m-%d %H:%M')
        headers = (
            'Код', 'Артикул', 'Наименование', 'Производитель', 'Ед', 'Цена,руб', 'Остаток', 'Заявка',
            'Норма отпуска', 'Новинка!')
        data = []
        data = tablib.Dataset(data, headers=headers, title=price_title)
        products = Product.objects.filter(stock__gt=0, published=True).select_related('brand',
                                                                                      'product_sklad').prefetch_related(
            'brand__clt1__client_id__discount_list_k1', 'category__parent', ).exclude(
            product_sklad__gte=1)
        saleban = Salenban.objects.filter(client_id=request.user.usrid.listusers_id,
                                          published=1).select_related('client_id',
                                                                      'mf_un').prefetch_related(
            'client_id__discount_list_k1').values_list('mf_un', flat=True)
        post = []
        for person in saleban:
            post.append(person)
        if saleban.exists():
            for pos in post:
                products = products.filter.exclude(brand=pos)
        else:
            products = products
        for book in products:
            zayvka = ""
            if book.artikul == "None":
                book.artikul = ""
            if book.brand == "None":
                book.brand = ""
            if book.new_product == 0:
                book.new_product = ""
            if book.stock > 50:
                book.stock = ">50"
            data.append((book.id_p, book.artikul, book.name, book.brand, book.shtuk_e, round(book.user_max2(), 2),
                         book.stock, zayvka, book.min_kol, book.new_product))
        def zip(src, dst):
            zf = zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED, compresslevel = 9)
            abs_src = os.path.abspath(src)
            for dirname, subdirs, files in os.walk(src):
                for filename in files:
                    absname = os.path.abspath(os.path.join(dirname, filename))
                    arcname = absname[len(abs_src) + 1:]
                    print('zipping %s as %s' % (os.path.join(dirname, filename),arcname))
                    zf.write(absname, arcname)
            zf.close()
        with open("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx/price_" + password + ".xlsx",
                  "wb") as f_price:
            f_price.write(data.export('xlsx'))
            f_price.close()
            del f_price
        dst = "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip/price_" + password + ".zip"
        src =  "/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/xlsx/"
        zip(src, dst)
        #os.remove("/var/www/django/mysite/static/media/pricenew/" + str(request.user.usrid.listusers_id) + "/zip/price.xlsx")
        del data
        del post
        del headers
        del book
        del price_title
        del products
        del saleban
        gc.collect()
        url = "https://localhost/media/pricenew/" + str(
            request.user.usrid.listusers_id) + "/zip/price_" + password + ".zip"
        return JsonResponse({'pricesecret': password + ".zip", 'url': url})

python-3.x django
1个回答
0
投票

虽然有点难以判断,但我认为您可能会考虑流读取和写入数据。这应该/可能会生成一个对给定客户端在一小时内有效的 CSV。我还没有测试过这个,所以它可能充满错误。

这也只会生成原始 CSV 文件而不是 zip 文件,如果您需要最终结果是 zip 文件,那么您可以使用

.tmp
名称创建它,并在完成后重命名。

这里的关键是希望我们永远不需要将完整的产品数据立即加载到内存中。

import os
import pathlib
import datetime
import datetime
import hashlib
import csv

from django.http import JsonResponse

##-----------------------------
## Round time to nearest hour
## https://stackoverflow.com/a/48938464/218663
##-----------------------------
def hour_rounder(t):
    return (
        t.replace(second=0, microsecond=0, minute=0, hour=t.hour)
        + datetime.timedelta(hours=t.minute//30)
    )
##-----------------------------

@sync_to_async
@login_required
def export_data(request):
    client_id = request.user.usrid.listusers_id
    root_folder = "/var/www/django/mysite/static/media/pricenew/"
    target_folder = f"{root_folder}{client_id}"
    current_hour = hour_rounder(datetime.datetime.now())
    current_slug = hashlib.sha256(str(current_hour).encode()).hexdigest()
    target_file_name  = f"prince_{current_slug}.csv"
    target_file_path = f"{target_folder}/{target_file_name}"
    root_url = "https://localhost/media/pricenew/"
    target_url = f"{root_url}{client_id}/{target_file_name}"

    ##------------------------
    ## if a recent export exists return the data for it
    ##------------------------
    if os.path.exists(target_file_path):
        return JsonResponse({"pricesecret": target_file_name, "url": target_url})
    ##------------------------

    ##------------------------
    ## make sure we have a target folder to write to
    ##------------------------
    pathlib.Path(target_folder).mkdir(parents=True, exist_ok=True)
    ##------------------------

    ##------------------------
    ## get the names we want to exclude as a set
    ##------------------------
    saleban = set(Salenban
        .objects
        .filter(client_id=client_id, published=1)
        .select_related("client_id", "mf_un")
        .prefetch_related("client_id__discount_list_k1")
        .values_list("mf_un", flat=True)
    )
    ##------------------------

    ##------------------------
    ##  interesting products
    ##------------------------
    products = (Product
        .objects
        .filter(stock__gt=0, published=True)
        .select_related("brand","product_sklad")
        .prefetch_related("brand__clt1__client_id__discount_list_k1", "category__parent")
        .exclude(product_sklad__gte=1)
    )
    ##------------------------

    ##------------------------
    ## a list of CSV headers
    ##------------------------
    headers = [
        "Код",
        "Артикул",
        "Наименование",
        "Производитель",
        "Ед",
        "Цена,руб",
        "Остаток",
        "Заявка",
        "Норма отпуска",
        "Новинка!"
    ]
    ##------------------------

    ##------------------------
    ## stream write to a temporary CSV path
    ##------------------------
    with open(target_file_path + ".tmp",  "w", encoding="utf-8", newline="") as f_price:
        writer = csv.writer(f_price)
        writer.writerow(headers)
        for book in products:
            if book.brand in saleban:
                continue

            writer.writerow([
                book.id_p,
                "" if book.artikul == "None" else book.artikul,
                book.name,
                "" if book.brand == "None" else book.brand,
                book.shtuk_e,
                round(book.user_max2(), 2),
                ">50" if book.stock > 50 else book.stock,
                "",
                book.min_kol,
                "" if book.new_product == 0 else book.new_product
            ])
    ##------------------------

    ##------------------------
    ## Now that the file is complete, rename it to expose it.
    ##------------------------
    os.rename(target_file_path + ".tmp", target_file_path)
    ##------------------------

    return JsonResponse({"pricesecret": target_file_name, "url": target_url})
© www.soinside.com 2019 - 2024. All rights reserved.