Django

Django

Django is an open source server-side web application framework, written in Python. It is designed to reduce the effort required to create complex data-driven websites and web applications, with a special focus on less code, no-redundancy and being more explicit than implicit.
Hoang Tran

Hoang Tran

1656861060

Bắt Đầu Với Chế Độ Xem Không Đồng Bộ Của Django

Viết mã không đồng bộ cung cấp cho bạn khả năng tăng tốc ứng dụng của mình mà không tốn nhiều công sức. Các phiên bản Django> = 3.1 hỗ trợ chế độ xem không đồng bộ, phần mềm trung gian và các thử nghiệm. Nếu bạn chưa thử nghiệm với chế độ xem không đồng bộ, bây giờ là thời điểm tuyệt vời để bạn có được chúng.

Hướng dẫn này xem xét cách bắt đầu với các chế độ xem không đồng bộ của Django.

Nếu bạn quan tâm đến việc tìm hiểu thêm về sức mạnh đằng sau mã không đồng bộ cùng với sự khác biệt giữa các luồng, đa xử lý và không đồng bộ trong Python, hãy xem bài viết Tăng tốc Python với Concurrency, Parallelism và asyncio của tôi .

Mục tiêu

Đến cuối hướng dẫn này, bạn sẽ có thể:

  1. Viết chế độ xem không đồng bộ trong Django
  2. Thực hiện một yêu cầu HTTP không chặn trong chế độ xem Django
  3. Đơn giản hóa các tác vụ nền cơ bản với chế độ xem không đồng bộ của Django
  4. Sử dụng sync_to_asyncđể thực hiện cuộc gọi đồng bộ bên trong chế độ xem không đồng bộ
  5. Giải thích khi nào bạn nên và không nên sử dụng chế độ xem không đồng bộ

Bạn cũng có thể trả lời các câu hỏi sau:

  1. Điều gì sẽ xảy ra nếu bạn thực hiện cuộc gọi đồng bộ bên trong chế độ xem không đồng bộ?
  2. Điều gì sẽ xảy ra nếu bạn thực hiện một cuộc gọi đồng bộ và không đồng bộ bên trong một chế độ xem không đồng bộ?
  3. Cần tây có còn cần thiết với các chế độ xem không đồng bộ của Django không?

Điều kiện tiên quyết

Miễn là bạn đã quen thuộc với chính Django, việc thêm chức năng không đồng bộ vào các chế độ xem không dựa trên lớp là cực kỳ đơn giản.

Sự phụ thuộc

  1. Python> = 3,10
  2. Django> = 4.0
  3. Uvicorn
  4. HTTPX

ASGI là gì?

ASGI là viết tắt của Asynchronous Server Gateway Interface. Đó là phiên bản tiếp theo không đồng bộ, hiện đại đối với WSGI , cung cấp một tiêu chuẩn để tạo các ứng dụng web dựa trên Python không đồng bộ.

Một điều đáng nói khác là ASGI tương thích ngược với WSGI, đây là lý do tốt để chuyển từ máy chủ WSGI như Gunicorn hoặc uWSGI sang máy chủ ASGI như Uvicorn hoặc Daphne ngay cả khi bạn chưa sẵn sàng chuyển sang viết ứng dụng không đồng bộ .

Tạo ứng dụng

Tạo một thư mục dự án mới cùng với một dự án Django mới:

$ mkdir django-async-views && cd django-async-views
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject hello_async .

Hãy trao đổi virtualenv và Pip cho thơ hoặc Pipenv . Để biết thêm, hãy xem lại Môi trường Python hiện đại .

Django sẽ chạy các chế độ xem không đồng bộ của bạn nếu bạn đang sử dụng máy chủ phát triển tích hợp sẵn, nhưng nó sẽ không thực sự chạy chúng một cách không đồng bộ, vì vậy chúng tôi sẽ chạy Django với Uvicorn.

Cài đặt nó:

(env)$ pip install uvicorn

Để chạy dự án của bạn với Uvicorn, bạn sử dụng lệnh sau từ thư mục gốc của dự án:

uvicorn {name of your project}.asgi:application

Trong trường hợp của chúng tôi, đây sẽ là:

(env)$ uvicorn hello_async.asgi:application

Tiếp theo, hãy tạo chế độ xem không đồng bộ đầu tiên của chúng ta. Thêm tệp mới để giữ các dạng xem của bạn trong thư mục "hello_async", rồi thêm dạng xem sau:

# hello_async/views.py

from django.http import HttpResponse


async def index(request):
    return HttpResponse("Hello, async Django!")

Tạo chế độ xem không đồng bộ trong Django cũng đơn giản như tạo chế độ xem đồng bộ - tất cả những gì bạn cần làm là thêm asynctừ khóa.

Cập nhật các URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
]

Bây giờ, trong một thiết bị đầu cuối, trong thư mục gốc của bạn, hãy chạy:

(env)$ uvicorn hello_async.asgi:application --reload

Cờ --reloadyêu cầu Uvicorn xem các tệp của bạn để biết các thay đổi và tải lại nếu tìm thấy bất kỳ thay đổi nào. Điều đó có lẽ đã tự giải thích.

Mở http: // localhost: 8000 / trong trình duyệt web yêu thích của bạn:

Hello, async Django!

Không phải là điều thú vị nhất trên thế giới, nhưng, đây là một sự khởi đầu. Cần lưu ý rằng việc chạy chế độ xem này với máy chủ phát triển tích hợp sẵn của Django sẽ dẫn đến chức năng và đầu ra giống hệt nhau. Điều này là do chúng tôi không thực sự làm bất kỳ điều gì không đồng bộ trong trình xử lý.

HTTPX

Cần lưu ý rằng hỗ trợ không đồng bộ hoàn toàn tương thích ngược, vì vậy bạn có thể kết hợp chế độ xem không đồng bộ và đồng bộ hóa, phần mềm trung gian và thử nghiệm. Django sẽ thực thi từng thứ trong ngữ cảnh thực thi thích hợp.

Để chứng minh điều này, hãy thêm một vài chế độ xem mới:

# hello_async/views.py

import asyncio
from time import sleep

import httpx
from django.http import HttpResponse


# helpers

async def http_call_async():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)


def http_call_sync():
    for num in range(1, 6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)


# views

async def index(request):
    return HttpResponse("Hello, async Django!")


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")


def sync_view(request):
    http_call_sync()
    return HttpResponse("Blocking HTTP request")

Cập nhật các URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view


urlpatterns = [
    path("admin/", admin.site.urls),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Cài đặt HTTPX :

(env)$ pip install httpx

Khi máy chủ đang chạy, điều hướng đến http: // localhost: 8000 / async / . Bạn sẽ thấy ngay phản hồi:

Non-blocking HTTP request

Trong thiết bị đầu cuối của bạn, bạn sẽ thấy:

INFO:     127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Tại đây, phản hồi HTTP được gửi lại trước lệnh gọi ngủ đầu tiên.

Tiếp theo, điều hướng đến http: // localhost: 8000 / sync / . Sẽ mất khoảng năm giây để nhận được phản hồi:

Blocking HTTP request

Chuyển sang thiết bị đầu cuối:

1
2
3
4
5
<Response [200 OK]>
INFO:     127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK

Tại đây, phản hồi HTTP được gửi sau vòng lặp và yêu cầu https://httpbin.org/hoàn tất.

Hút một số loại thịt

Để mô phỏng thêm một kịch bản trong thế giới thực về cách bạn tận dụng tính năng không đồng bộ, hãy xem cách chạy nhiều hoạt động không đồng bộ, tổng hợp kết quả và trả lại chúng cho người gọi.

Quay lại URLconf của dự án của bạn, tạo một đường dẫn mới tại smoke_some_meats:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Quay lại dạng xem của bạn, tạo một hàm trợ giúp không đồng bộ mới được gọi là smoke. Hàm này nhận hai tham số: danh sách các chuỗi được gọi smokablesvà một chuỗi được gọi flavor. Những thứ này được mặc định là danh sách các loại thịt hun khói và "Sweet Baby Ray's", tương ứng.

# hello_async/views.py

async def smoke(smokables: List[str] = None, flavor: str = "Sweet Baby Ray's") -> List[str]:
    """ Smokes some meats and applies the Sweet Baby Ray's """

    for smokable in smokables:
        print(f"Smoking some {smokable}...")
        print(f"Applying the {flavor}...")
        print(f"{smokable.capitalize()} smoked.")

    return len(smokables)

Vòng lặp for áp dụng một cách không đồng bộ hương vị (đọc là: Sweet Baby Ray's) cho các loại thịt hun khói (đọc là: thịt hun khói).

Đừng quên nhập:

from typing import List

Listđược sử dụng cho khả năng đánh máy bổ sung. Điều này là không bắt buộc và có thể dễ dàng bị bỏ qua (chỉ cần bỏ qua phần : List[str]khai báo tham số "smokingables" sau đây).

Tiếp theo, thêm hai trình trợ giúp không đồng bộ khác:

async def get_smokables():
    print("Getting smokeables...")

    await asyncio.sleep(2)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning smokeable")
        return [
            "ribs",
            "brisket",
            "lemon chicken",
            "salmon",
            "bison sirloin",
            "sausage",
        ]


async def get_flavor():
    print("Getting flavor...")

    await asyncio.sleep(1)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning flavor")
        return random.choice(
            [
                "Sweet Baby Ray's",
                "Stubb's Original",
                "Famous Dave's",
            ]
        )

Đảm bảo thêm nhập:

import random

Tạo dạng xem không đồng bộ sử dụng các chức năng không đồng bộ:

# hello_async/views.py

async def smoke_some_meats(request):
    results = await asyncio.gather(*[get_smokables(), get_flavor()])
    total = await asyncio.gather(*[smoke(results[0], results[1])])
    return HttpResponse(f"Smoked {total[0]} meats with {results[1]}!")

Dạng xem này gọi hàm get_smokablesget_flavorđồng thời. Vì smokephụ thuộc vào kết quả từ cả hai get_smokablesget_flavor, chúng tôi đã từng gatherđợi mỗi tác vụ không đồng bộ hoàn thành.

Hãy nhớ rằng điều đó trong chế độ xem đồng bộ hóa thông thường get_smokablesget_flavorsẽ được xử lý từng cái một. Ngoài ra, chế độ xem không đồng bộ sẽ mang lại hiệu quả thực thi và cho phép các yêu cầu khác được xử lý trong khi các tác vụ không đồng bộ được xử lý, cho phép nhiều yêu cầu hơn được xử lý bởi cùng một quy trình trong một khoảng thời gian cụ thể.

Cuối cùng, một phản hồi được trả lại để cho người dùng biết rằng bữa ăn BBQ ngon của họ đã sẵn sàng.

Tuyệt quá. Lưu tệp, sau đó quay lại trình duyệt của bạn và điều hướng đến http: // localhost: 8000 / khói_some_meats / . Sẽ mất vài giây để nhận được phản hồi:

Smoked 6 meats with Sweet Baby Ray's!

Trong bảng điều khiển của mình, bạn sẽ thấy:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Smoking some ribs...
Applying the Stubb's Original...
Ribs smoked.
Smoking some brisket...
Applying the Stubb's Original...
Brisket smoked.
Smoking some lemon chicken...
Applying the Stubb's Original...
Lemon chicken smoked.
Smoking some salmon...
Applying the Stubb's Original...
Salmon smoked.
Smoking some bison sirloin...
Applying the Stubb's Original...
Bison sirloin smoked.
Smoking some sausage...
Applying the Stubb's Original...
Sausage smoked.
INFO:     127.0.0.1:57501 - "GET /smoke_some_meats/ HTTP/1.1" 200 OK

Hãy lưu ý thứ tự của các câu lệnh in sau:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Đây là sự không đồng bộ khi làm việc: Khi get_smokableshàm ở chế độ ngủ, get_flavorhàm sẽ kết thúc quá trình xử lý.

Thịt cháy

Đồng bộ hóa cuộc gọi

Hỏi: Điều gì sẽ xảy ra nếu bạn thực hiện cuộc gọi đồng bộ bên trong chế độ xem không đồng bộ?

Điều tương tự sẽ xảy ra nếu bạn gọi một hàm không đồng bộ từ một chế độ xem không đồng bộ.

--

Để minh họa điều này, hãy tạo một hàm trợ giúp mới trong views.py của bạn được gọi là oversmoke:

# hello_async/views.py

def oversmoke() -> None:
    """ If it's not dry, it must be uncooked """
    sleep(5)
    print("Who doesn't love burnt meats?")

Rất đơn giản: Chúng tôi chỉ đợi đồng bộ trong năm giây.

Tạo dạng xem gọi hàm này:

# hello_async/views.py

async def burn_some_meats(request):
    oversmoke()
    return HttpResponse(f"Burned some meats.")

Cuối cùng, sắp xếp lộ trình trong URLconf của dự án của bạn:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats, burn_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Truy cập tuyến đường trong trình duyệt tại http: // localhost: 8000 / burn_some_meats :

Burned some meats.

Lưu ý rằng phải mất năm giây để cuối cùng nhận được phản hồi từ trình duyệt. Bạn cũng nên nhận được đầu ra bảng điều khiển cùng một lúc:

Who doesn't love burnt meats?
INFO:     127.0.0.1:40682 - "GET /burn_some_meats HTTP/1.1" 200 OK

Có thể đáng lưu ý rằng điều tương tự sẽ xảy ra bất kể máy chủ bạn đang sử dụng, có thể là WSGI hoặc dựa trên ASGI.

Đồng bộ hóa và không đồng bộ hóa cuộc gọi

Hỏi: Điều gì sẽ xảy ra nếu bạn thực hiện một cuộc gọi đồng bộ và không đồng bộ bên trong một chế độ xem không đồng bộ?

Đừng làm điều này.

Chế độ xem đồng bộ và không đồng bộ có xu hướng hoạt động tốt nhất cho các mục đích khác nhau. Nếu bạn có chức năng chặn trong chế độ xem không đồng bộ, thì tốt nhất nó sẽ không tốt hơn là chỉ sử dụng chế độ xem đồng bộ.

Đồng bộ với không đồng bộ

Nếu bạn cần thực hiện cuộc gọi đồng bộ bên trong dạng xem không đồng bộ (ví dụ như tương tác với cơ sở dữ liệu thông qua Django ORM), hãy sử dụng sync_to_async làm trình bao bọc hoặc trình trang trí.

Thí dụ:

# hello_async/views.py

async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync, thread_sensitive=False)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request (via sync_to_async)")

Bạn có nhận thấy rằng chúng tôi đã đặt thread_sensitivethông số thành Falsekhông? Điều này có nghĩa là hàm đồng bộ http_call_sync, sẽ được chạy trong một luồng mới. Xem lại các tài liệu để biết thêm thông tin.

Thêm nhập vào đầu:

from asgiref.sync import sync_to_async

Thêm URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import (
    index,
    async_view,
    sync_view,
    smoke_some_meats,
    burn_some_meats,
    async_with_sync_view
)


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("sync_to_async/", async_with_sync_view),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Kiểm tra nó trong trình duyệt của bạn tại http: // localhost: 8000 / sync_to_async / .

Trong thiết bị đầu cuối của bạn, bạn sẽ thấy:

INFO:     127.0.0.1:61365 - "GET /sync_to_async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Bằng cách sử dụng sync_to_async, cuộc gọi đồng bộ chặn đã được xử lý trong một chuỗi nền, cho phép phản hồi HTTP được gửi lại trước cuộc gọi ngủ đầu tiên.

Chế độ xem cần tây và không đồng bộ

Q: Celery có còn cần thiết với các chế độ xem không đồng bộ của Django không?

Nó phụ thuộc.

Các chế độ xem không đồng bộ của Django cung cấp chức năng tương tự như một tác vụ hoặc hàng đợi tin nhắn mà không phức tạp. Nếu bạn đang sử dụng (hoặc đang cân nhắc) Django và muốn làm điều gì đó đơn giản (và không quan tâm đến độ tin cậy), chế độ xem không đồng bộ là một cách tuyệt vời để thực hiện điều này một cách nhanh chóng và dễ dàng. Nếu bạn cần thực hiện các quy trình nền chạy lâu và nặng hơn nhiều, bạn sẽ vẫn muốn sử dụng Celery hoặc RQ.

Cần lưu ý rằng để sử dụng các chế độ xem không đồng bộ một cách hiệu quả, bạn chỉ nên có các lệnh gọi không đồng bộ trong chế độ xem. Mặt khác, hàng đợi tác vụ sử dụng công nhân trên các quy trình riêng biệt và do đó có khả năng chạy các lệnh gọi đồng bộ trong nền, trên nhiều máy chủ.

Nhân tiện, bạn không phải chọn giữa chế độ xem không đồng bộ và hàng đợi tin nhắn - bạn có thể dễ dàng sử dụng chúng song song. Ví dụ: Bạn có thể sử dụng chế độ xem không đồng bộ để gửi email hoặc thực hiện sửa đổi cơ sở dữ liệu một lần, nhưng hãy yêu cầu Celery dọn dẹp cơ sở dữ liệu của bạn vào thời gian đã định mỗi đêm hoặc tạo và gửi báo cáo khách hàng.

Khi nào sử dụng

Đối với các dự án greenfield, nếu không đồng bộ là sở thích của bạn, hãy tận dụng các chế độ xem không đồng bộ và viết các quy trình I / O của bạn theo cách không đồng bộ càng nhiều càng tốt. Điều đó nói rằng, nếu hầu hết các chế độ xem của bạn chỉ cần thực hiện lệnh gọi đến cơ sở dữ liệu và thực hiện một số xử lý cơ bản trước khi trả lại dữ liệu, bạn sẽ không thấy sự gia tăng nhiều (nếu có) so với việc chỉ gắn bó với các chế độ xem đồng bộ.

Đối với các dự án brownfield, nếu bạn có ít hoặc không có quá trình I / O thì hãy gắn với chế độ xem đồng bộ. Nếu bạn có một số quy trình I / O, hãy kiểm tra xem bạn sẽ dễ dàng viết lại chúng theo cách không đồng bộ như thế nào. Viết lại I / O đồng bộ hóa thành không đồng bộ không dễ dàng, vì vậy có thể bạn sẽ muốn tối ưu hóa I / O đồng bộ hóa và chế độ xem của mình trước khi cố gắng viết lại thành không đồng bộ. Ngoài ra, không bao giờ là một ý tưởng hay khi kết hợp các quy trình đồng bộ hóa với các chế độ xem không đồng bộ của bạn.

Trong quá trình sản xuất, hãy đảm bảo sử dụng Gunicorn để quản lý Uvicorn nhằm tận dụng lợi thế của cả đồng thời (thông qua Uvicorn) và song song (thông qua Gunicorn worker):

gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application

Sự kết luận

Tóm lại, mặc dù đây là một trường hợp sử dụng đơn giản, nhưng nó sẽ cung cấp cho bạn một ý tưởng sơ bộ về các khả năng mà các khung nhìn không đồng bộ của Django mở ra. Một số điều khác cần thử trong chế độ xem không đồng bộ của bạn là gửi email, gọi API của bên thứ ba và đọc / ghi vào tệp.

Nguồn:  https://testdriven.io

#django #async 

Bắt Đầu Với Chế Độ Xem Không Đồng Bộ Của Django

Как начать работу с асинхронными представлениями Django

Написание асинхронного кода дает вам возможность ускорить ваше приложение без особых усилий. Версии Django >= 3.1 поддерживают асинхронные представления, промежуточное ПО и тесты. Если вы еще не экспериментировали с асинхронными представлениями, сейчас самое время попробовать их.

В этом руководстве показано, как начать работу с асинхронными представлениями Django.

Если вам интересно узнать больше о силе асинхронного кода, а также о различиях между потоками, многопроцессорностью и асинхронностью в Python, ознакомьтесь с моей статьей «Ускорение Python с помощью параллелизма, параллелизма и асинхронности ».

Цели

К концу этого урока вы должны уметь:

  1. Напишите асинхронное представление в Django.
  2. Сделать неблокирующий HTTP-запрос в представлении Django
  3. Упростите основные фоновые задачи с помощью асинхронных представлений Django.
  4. Используйте sync_to_asyncдля синхронного вызова внутри асинхронного представления.
  5. Объясните, когда вы должны и не должны использовать асинхронные представления

Вы также должны быть в состоянии ответить на следующие вопросы:

  1. Что, если вы сделаете синхронный вызов внутри асинхронного представления?
  2. Что, если вы сделаете синхронный и асинхронный вызов внутри асинхронного представления?
  3. Нужен ли Celery с асинхронными представлениями Django?

Предпосылки

Если вы уже знакомы с самим Django, добавление асинхронной функциональности к представлениям, не основанным на классах, чрезвычайно просто.

Зависимости

  1. Питон >= 3.10
  2. Джанго >= 4.0
  3. Увикорн
  4. HTTPX

Что такое АСГИ?

ASGI означает асинхронный интерфейс шлюза сервера. Это современное асинхронное продолжение WSGI , предоставляющее стандарт для создания асинхронных веб-приложений на основе Python.

Еще одна вещь, о которой стоит упомянуть, это то, что ASGI обратно совместим с WSGI, что делает его хорошим предлогом для перехода с сервера WSGI, такого как Gunicorn или uWSGI, на сервер ASGI, такой как Uvicorn или Daphne , даже если вы не готовы переключиться на написание асинхронных приложений . .

Создание приложения

Создайте новый каталог проекта вместе с новым проектом Django:

$ mkdir django-async-views && cd django-async-views
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject hello_async .

Не стесняйтесь менять virtualenv и Pip на Poetry или Pipenv . Подробнее см. в Modern Python Environments .

Django будет запускать ваши асинхронные представления, если вы используете встроенный сервер разработки, но на самом деле он не будет запускать их асинхронно, поэтому мы запустим Django с Uvicorn.

Установите его:

(env)$ pip install uvicorn

Чтобы запустить ваш проект с помощью Uvicorn, вы используете следующую команду из корня вашего проекта:

uvicorn {name of your project}.asgi:application

В нашем случае это будет:

(env)$ uvicorn hello_async.asgi:application

Далее давайте создадим наше первое асинхронное представление. Добавьте новый файл для хранения представлений в папку «hello_async», а затем добавьте следующее представление:

# hello_async/views.py

from django.http import HttpResponse


async def index(request):
    return HttpResponse("Hello, async Django!")

Создание асинхронных представлений в Django так же просто, как создание синхронных представлений — все, что вам нужно сделать, это добавить asyncключевое слово.

Обновите URL-адреса:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
]

Теперь в терминале в корневой папке запустите:

(env)$ uvicorn hello_async.asgi:application --reload

Флаг --reloadуказывает Uvicorn следить за изменениями в ваших файлах и перезагружать их, если они будут обнаружены. Это, наверное, было само собой разумеющимся.

Откройте http://localhost:8000/ в своем любимом веб-браузере:

Hello, async Django!

Не самая захватывающая вещь в мире, но, эй, это начало. Стоит отметить, что запуск этого представления со встроенным сервером разработки Django приведет к точно такой же функциональности и результату. Это потому, что мы на самом деле не делаем ничего асинхронного в обработчике.

HTTPX

Стоит отметить, что асинхронная поддержка полностью совместима с предыдущими версиями, поэтому вы можете комбинировать асинхронные и синхронные представления, промежуточное ПО и тесты. Django выполнит каждый в соответствующем контексте выполнения.

Чтобы продемонстрировать это, добавьте несколько новых представлений:

# hello_async/views.py

import asyncio
from time import sleep

import httpx
from django.http import HttpResponse


# helpers

async def http_call_async():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)


def http_call_sync():
    for num in range(1, 6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)


# views

async def index(request):
    return HttpResponse("Hello, async Django!")


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")


def sync_view(request):
    http_call_sync()
    return HttpResponse("Blocking HTTP request")

Обновите URL-адреса:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view


urlpatterns = [
    path("admin/", admin.site.urls),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Установите HTTPX :

(env)$ pip install httpx

Когда сервер запущен, перейдите по адресу http://localhost:8000/async/ . Вы должны сразу увидеть ответ:

Non-blocking HTTP request

В вашем терминале вы должны увидеть:

INFO:     127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Здесь ответ HTTP отправляется обратно перед первым вызовом сна.

Затем перейдите по адресу http://localhost:8000/sync/ . Получение ответа должно занять около пяти секунд:

Blocking HTTP request

Повернитесь к терминалу:

1
2
3
4
5
<Response [200 OK]>
INFO:     127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK

Здесь ответ HTTP отправляется после завершения цикла и завершения запроса https://httpbin.org/.

Курение мяса

Чтобы смоделировать более реальный сценарий использования асинхронности, давайте рассмотрим, как асинхронно выполнять несколько операций, агрегировать результаты и возвращать их вызывающему объекту.

Вернувшись в URLconf вашего проекта, создайте новый путь по адресу smoke_some_meats:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Вернувшись в представления, создайте новую вспомогательную асинхронную функцию с именем smoke. Эта функция принимает два параметра: список вызываемых строк smokablesи строку с именем flavor. По умолчанию это список копченого мяса и «Sweet Baby Ray's» соответственно.

# hello_async/views.py

async def smoke(smokables: List[str] = None, flavor: str = "Sweet Baby Ray's") -> List[str]:
    """ Smokes some meats and applies the Sweet Baby Ray's """

    for smokable in smokables:
        print(f"Smoking some {smokable}...")
        print(f"Applying the {flavor}...")
        print(f"{smokable.capitalize()} smoked.")

    return len(smokables)

Цикл for асинхронно применяет вкус (читай: Sweet Baby Ray's) к куримым (читай: копченостям).

Не забудьте импорт:

from typing import List

Listиспользуется для дополнительных возможностей набора текста. Это не требуется и может быть легко опущено (просто : List[str]отмените следующее объявление параметра "smokables").

Затем добавьте еще два асинхронных помощника:

async def get_smokables():
    print("Getting smokeables...")

    await asyncio.sleep(2)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning smokeable")
        return [
            "ribs",
            "brisket",
            "lemon chicken",
            "salmon",
            "bison sirloin",
            "sausage",
        ]


async def get_flavor():
    print("Getting flavor...")

    await asyncio.sleep(1)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning flavor")
        return random.choice(
            [
                "Sweet Baby Ray's",
                "Stubb's Original",
                "Famous Dave's",
            ]
        )

Обязательно добавьте импорт:

import random

Создайте асинхронное представление, использующее асинхронные функции:

# hello_async/views.py

async def smoke_some_meats(request):
    results = await asyncio.gather(*[get_smokables(), get_flavor()])
    total = await asyncio.gather(*[smoke(results[0], results[1])])
    return HttpResponse(f"Smoked {total[0]} meats with {results[1]}!")

Это представление вызывает функции get_smokablesи get_flavorодновременно. Поскольку smokeэто зависит от результатов как от , так get_smokablesи от get_flavor, мы обычно gatherждали завершения каждой асинхронной задачи.

Имейте в виду, что в обычном представлении синхронизации get_smokablesи get_flavorбудут обрабатываться по одному. Кроме того, асинхронное представление приведет к выполнению и позволит обрабатывать другие запросы во время обработки асинхронных задач, что позволяет обрабатывать большее количество запросов одним и тем же процессом за определенное время.

Наконец, возвращается ответ, информирующий пользователя о том, что вкусная еда для барбекю готова.

Большой. Сохраните файл, затем вернитесь в браузер и перейдите по адресу http://localhost:8000/smoke_some_meats/ . Получение ответа должно занять несколько секунд:

Smoked 6 meats with Sweet Baby Ray's!

В консоли вы должны увидеть:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Smoking some ribs...
Applying the Stubb's Original...
Ribs smoked.
Smoking some brisket...
Applying the Stubb's Original...
Brisket smoked.
Smoking some lemon chicken...
Applying the Stubb's Original...
Lemon chicken smoked.
Smoking some salmon...
Applying the Stubb's Original...
Salmon smoked.
Smoking some bison sirloin...
Applying the Stubb's Original...
Bison sirloin smoked.
Smoking some sausage...
Applying the Stubb's Original...
Sausage smoked.
INFO:     127.0.0.1:57501 - "GET /smoke_some_meats/ HTTP/1.1" 200 OK

Обратите внимание на порядок следующих операторов печати:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Это асинхронность в действии: пока get_smokablesфункция засыпает, get_flavorфункция завершает обработку.

Пригоревшее мясо

Синхронизация вызова

Вопрос. Что делать, если вы делаете синхронный вызов внутри асинхронного представления?

То же самое произойдет, если вы вызовете неасинхронную функцию из неасинхронного представления.

--

Чтобы проиллюстрировать это, создайте новую вспомогательную функцию в файле views.py с именем oversmoke:

# hello_async/views.py

def oversmoke() -> None:
    """ If it's not dry, it must be uncooked """
    sleep(5)
    print("Who doesn't love burnt meats?")

Очень просто: мы просто синхронно ждем пять секунд.

Создайте представление, которое вызывает эту функцию:

# hello_async/views.py

async def burn_some_meats(request):
    oversmoke()
    return HttpResponse(f"Burned some meats.")

Наконец, подключите маршрут в URLconf вашего проекта:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats, burn_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Посетите маршрут в браузере по адресу http://localhost:8000/burn_some_meats :

Burned some meats.

Обратите внимание, что потребовалось пять секунд, чтобы наконец получить ответ от браузера. Вы также должны были получить вывод консоли одновременно:

Who doesn't love burnt meats?
INFO:     127.0.0.1:40682 - "GET /burn_some_meats HTTP/1.1" 200 OK

Возможно, стоит отметить, что одно и то же произойдет независимо от используемого вами сервера, будь то WSGI или ASGI.

Синхронные и асинхронные вызовы

Вопрос. Что делать, если вы выполняете синхронный и асинхронный вызов внутри асинхронного представления?

Не делай этого.

Синхронные и асинхронные представления лучше всего подходят для разных целей. Если у вас есть функция блокировки в асинхронном представлении, в лучшем случае это будет не лучше, чем просто использование синхронного представления.

Синхронизировать с асинхронным

Если вам нужно сделать синхронный вызов внутри асинхронного представления (например, для взаимодействия с базой данных через ORM Django), используйте sync_to_async в качестве оболочки или декоратора.

Пример:

# hello_async/views.py

async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync, thread_sensitive=False)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request (via sync_to_async)")

Вы заметили, что мы установили для thread_sensitiveпараметра значение False? Это означает, что синхронная функция http_call_syncбудет выполняться в новом потоке. Просмотрите документы для получения дополнительной информации.

Добавьте импорт вверху:

from asgiref.sync import sync_to_async

Добавьте URL-адрес:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import (
    index,
    async_view,
    sync_view,
    smoke_some_meats,
    burn_some_meats,
    async_with_sync_view
)


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("sync_to_async/", async_with_sync_view),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Проверьте это в своем браузере по адресу http://localhost:8000/sync_to_async/ .

В вашем терминале вы должны увидеть:

INFO:     127.0.0.1:61365 - "GET /sync_to_async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

С помощью sync_to_asyncблокирующий синхронный вызов обрабатывался в фоновом потоке, что позволяло отправить ответ HTTP перед первым вызовом сна.

Celery и асинхронные представления

В: Нужен ли Celery для асинхронных представлений Django?

Это зависит.

Асинхронные представления Django предлагают функциональность, аналогичную задаче или очереди сообщений, но без сложности. Если вы используете (или рассматриваете) Django и хотите сделать что-то простое (и не заботитесь о надежности), асинхронные представления — отличный способ сделать это быстро и легко. Если вам нужно выполнять более тяжелые и длительные фоновые процессы, вы все равно захотите использовать Celery или RQ.

Следует отметить, что для эффективного использования асинхронных представлений в представлении должны быть только асинхронные вызовы. Очереди задач, с другой стороны, используют рабочие процессы в отдельных процессах и поэтому могут выполнять синхронные вызовы в фоновом режиме на нескольких серверах.

Кстати, вы ни в коем случае не должны выбирать между асинхронными представлениями и очередью сообщений — вы можете легко использовать их в тандеме. Например: вы можете использовать асинхронное представление для отправки электронной почты или внесения одноразовых изменений в базу данных, но заставить Celery очищать вашу базу данных в запланированное время каждую ночь или создавать и отправлять отчеты о клиентах.

Когда использовать

Для новых проектов, если вам нужна асинхронность, используйте асинхронные представления и пишите процессы ввода-вывода максимально асинхронно. Тем не менее, если большинству ваших представлений просто нужно сделать вызовы к базе данных и выполнить некоторую базовую обработку перед возвратом данных, вы не увидите большого увеличения (если оно вообще будет) по сравнению с простым использованием синхронизированных представлений.

Для старых проектов, если у вас практически нет процессов ввода-вывода, придерживайтесь синхронизированных представлений. Если у вас есть несколько процессов ввода-вывода, оцените, насколько легко будет переписать их асинхронно. Переписать синхронный ввод-вывод в асинхронный непросто, поэтому вы, вероятно, захотите оптимизировать свой синхронный ввод-вывод и представления, прежде чем пытаться переписать в асинхронный. Кроме того, никогда не рекомендуется смешивать процессы синхронизации с вашими асинхронными представлениями.

В рабочей среде обязательно используйте Gunicorn для управления Uvicorn, чтобы воспользоваться преимуществами как параллелизма (через Uvicorn), так и параллелизма (через воркеры Gunicorn):

gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application

Вывод

В заключение, хотя это был простой вариант использования, он должен дать вам общее представление о возможностях, которые открывают асинхронные представления Django. Некоторые другие вещи, которые можно попробовать в асинхронных представлениях, — это отправка электронных писем, вызов сторонних API и чтение из/запись в файлы.

Источник:  https://testdriven.io

#django #async 

Как начать работу с асинхронными представлениями Django

如何開始使用 Django 的異步視圖

編寫異步代碼使您能夠毫不費力地加快應用程序的速度。Django 版本 >= 3.1支持異步視圖、中間件和測試。如果您還沒有嘗試過異步視圖,那麼現在是掌握它們的好時機。

本教程著眼於如何開始使用 Django 的異步視圖。

如果您有興趣了解有關異步代碼背後的強大功能以及 Python 中線程、多處理和異步之間的區別的更多信息,請查看我的使用並發、並行和異步加速 Python文章。

目標

在本教程結束時,您應該能夠:

  1. 在 Django 中編寫異步視圖
  2. 在 Django 視圖中發出非阻塞 HTTP 請求
  3. 使用 Django 的異步視圖簡化基本的後台任務
  4. 用於sync_to_async在異步視圖中進行同步調用
  5. 解釋什麼時候應該和不應該使用異步視圖

您還應該能夠回答以下問題:

  1. 如果您在異步視圖中進行同步調用怎麼辦?
  2. 如果您在異步視圖中進行同步和異步調用怎麼辦?
  3. Django 的異步視圖仍然需要 Celery 嗎?

先決條件

只要您已經熟悉 Django 本身,向非基於類的視圖添加異步功能就非常簡單。

依賴項

  1. 蟒蛇> = 3.10
  2. Django >= 4.0
  3. 優維康
  4. HTTPX

什麼是 ASGI?

ASGI代表異步服務器網關接口。它是WSGI的現代異步後續版本,為創建基於 Python 的異步 Web 應用程序提供了標準。

另一件值得一提的是,ASGI 向後兼容 WSGI,這使得它成為從 Gunicorn 或 uWSGI 等 WSGI 服務器切換到UvicornDaphne等 ASGI 服務器的好藉口,即使你還沒有準備好切換到編寫異步應用程序.

創建應用程序

創建一個新的項目目錄以及一個新的 Django 項目:

$ mkdir django-async-views && cd django-async-views
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject hello_async .

隨意將 virtualenv 和 Pip 換成PoetryPipenv。有關更多信息,請查看現代 Python 環境

如果您使用內置的開發服務器,Django 將運行您的異步視圖,但它實際上不會異步運行它們,因此我們將使用 Uvicorn 運行 Django。

安裝它:

(env)$ pip install uvicorn

要使用 Uvicorn 運行項目,請從項目的根目錄中使用以下命令:

uvicorn {name of your project}.asgi:application

在我們的例子中,這將是:

(env)$ uvicorn hello_async.asgi:application

接下來,讓我們創建我們的第一個異步視圖。添加一個新文件以在“hello_async”文件夾中保存您的視圖,然後添加以下視圖:

# hello_async/views.py

from django.http import HttpResponse


async def index(request):
    return HttpResponse("Hello, async Django!")

在 Django 中創建異步視圖就像創建同步視圖一樣簡單——您需要做的就是添加async關鍵字。

更新網址:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
]

現在,在終端的根文件夾中,運行:

(env)$ uvicorn hello_async.asgi:application --reload

--reload標誌告訴 Uvicorn 監視您的文件是否有更改,如果發現任何更改則重新加載。這可能是不言自明的。

在您喜歡的 Web 瀏覽器中打開http://localhost:8000/ :

Hello, async Django!

這不是世界上最令人興奮的事情,但是,嘿,這是一個開始。值得注意的是,使用 Django 的內置開發服務器運行此視圖將產生完全相同的功能和輸出。這是因為我們實際上並沒有在處理程序中執行任何異步操作。

HTTPX

值得注意的是,異步支持完全向後兼容,因此您可以混合使用異步和同步視圖、中間件和測試。Django 將在正確的執行上下文中執行每一個。

為了證明這一點,添加一些新視圖:

# hello_async/views.py

import asyncio
from time import sleep

import httpx
from django.http import HttpResponse


# helpers

async def http_call_async():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)


def http_call_sync():
    for num in range(1, 6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)


# views

async def index(request):
    return HttpResponse("Hello, async Django!")


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")


def sync_view(request):
    http_call_sync()
    return HttpResponse("Blocking HTTP request")

更新網址:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view


urlpatterns = [
    path("admin/", admin.site.urls),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

安裝HTTPX

(env)$ pip install httpx

在服務器運行的情況下,導航到http://localhost:8000/async/。您應該立即看到響應:

Non-blocking HTTP request

在您的終端中,您應該看到:

INFO:     127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

在這裡,HTTP 響應在第一次睡眠調用之前被發回。

接下來,導航到http://localhost:8000/sync/。獲得響應大約需要五秒鐘:

Blocking HTTP request

轉到終端:

1
2
3
4
5
<Response [200 OK]>
INFO:     127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK

在這裡,HTTP 響應在循環之後發送並且請求https://httpbin.org/完成。

抽一些肉

為了模擬更多關於如何利用異步的真實場景,讓我們看看如何異步運行多個操作、聚合結果並將它們返回給調用者。

回到項目的 URLconf,在以下位置創建一個新路徑smoke_some_meats

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

回到您的視圖中,創建一個名為smoke. 這個函數有兩個參數:一個被調用的字符串列表smokables和一個被調用的字符串flavor。這些默認分別為可吸煙肉類和“Sweet Baby Ray's”列表。

# hello_async/views.py

async def smoke(smokables: List[str] = None, flavor: str = "Sweet Baby Ray's") -> List[str]:
    """ Smokes some meats and applies the Sweet Baby Ray's """

    for smokable in smokables:
        print(f"Smoking some {smokable}...")
        print(f"Applying the {flavor}...")
        print(f"{smokable.capitalize()} smoked.")

    return len(smokables)

for 循環將風味(閱讀:Sweet Baby Ray's)異步應用於可吸煙(閱讀:熏肉)。

不要忘記導入:

from typing import List

List用於額外的打字功能。這不是必需的,可以很容易地省略(只需: List[str]在“smokables”參數聲明後面加上)。

接下來,再添加兩個異步助手:

async def get_smokables():
    print("Getting smokeables...")

    await asyncio.sleep(2)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning smokeable")
        return [
            "ribs",
            "brisket",
            "lemon chicken",
            "salmon",
            "bison sirloin",
            "sausage",
        ]


async def get_flavor():
    print("Getting flavor...")

    await asyncio.sleep(1)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning flavor")
        return random.choice(
            [
                "Sweet Baby Ray's",
                "Stubb's Original",
                "Famous Dave's",
            ]
        )

確保添加導入:

import random

創建使用異步函數的異步視圖:

# hello_async/views.py

async def smoke_some_meats(request):
    results = await asyncio.gather(*[get_smokables(), get_flavor()])
    total = await asyncio.gather(*[smoke(results[0], results[1])])
    return HttpResponse(f"Smoked {total[0]} meats with {results[1]}!")

此視圖同時調用get_smokablesget_flavor函數。由於smoke依賴於 和 的結果get_smokablesget_flavor我們過去常常gather等待每個異步任務完成。

請記住,在常規同步視圖中,get_smokablesget_flavor一次處理一個。此外,異步視圖將產生執行並允許在處理異步任務時處理其他請求,這允許同一進程在特定時間內處理更多請求。

最後,返回一個響應,讓用戶知道他們準備好了美味的燒烤餐。

偉大的。保存文件,然後返回瀏覽器並導航到http://localhost:8000/smoke_some_meats/。需要幾秒鐘才能得到響應:

Smoked 6 meats with Sweet Baby Ray's!

在您的控制台中,您應該看到:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Smoking some ribs...
Applying the Stubb's Original...
Ribs smoked.
Smoking some brisket...
Applying the Stubb's Original...
Brisket smoked.
Smoking some lemon chicken...
Applying the Stubb's Original...
Lemon chicken smoked.
Smoking some salmon...
Applying the Stubb's Original...
Salmon smoked.
Smoking some bison sirloin...
Applying the Stubb's Original...
Bison sirloin smoked.
Smoking some sausage...
Applying the Stubb's Original...
Sausage smoked.
INFO:     127.0.0.1:57501 - "GET /smoke_some_meats/ HTTP/1.1" 200 OK

請注意以下打印語句的順序:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

這是工作中的異步性:當get_smokables函數休眠時,get_flavor函數完成處理。

燒焦的肉

同步通話

問:如果您在異步視圖中進行同步調用怎麼辦?

如果您從非異步視圖調用非異步函數,也會發生同樣的事情。

--

為了說明這一點,在您的views.py中創建一個新的輔助函數,名為oversmoke

# hello_async/views.py

def oversmoke() -> None:
    """ If it's not dry, it must be uncooked """
    sleep(5)
    print("Who doesn't love burnt meats?")

非常簡單:我們只是同步等待五秒鐘。

創建調用此函數的視圖:

# hello_async/views.py

async def burn_some_meats(request):
    oversmoke()
    return HttpResponse(f"Burned some meats.")

最後,在項目的 URLconf 中連接路由:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats, burn_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

在瀏覽器中訪問http://localhost:8000/burn_some_meats中的路由:

Burned some meats.

請注意最終從瀏覽器返迴響應花了五秒鐘。您還應該同時收到控制台輸出:

Who doesn't love burnt meats?
INFO:     127.0.0.1:40682 - "GET /burn_some_meats HTTP/1.1" 200 OK

可能值得注意的是,無論您使用哪種服務器,無論是基於 WSGI 還是基於 ASGI,都會發生同樣的事情。

同步和異步調用

問:如果您在異步視圖中進行同步和異步調用怎麼辦?

不要這樣做。

同步和異步視圖往往最適合不同的目的。如果您在異步視圖中具有阻塞功能,那麼充其量不會比僅使用同步視圖更好。

同步到異步

如果您需要在異步視圖中進行同步調用(例如,通過 Django ORM 與數據庫交互),請使用sync_to_async作為包裝器或裝飾器。

例子:

# hello_async/views.py

async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync, thread_sensitive=False)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request (via sync_to_async)")

您是否注意到我們將thread_sensitive參數設置為False?這意味著同步函數http_call_sync將在新線程中運行。查看文檔以獲取更多信息。

將導入添加到頂部:

from asgiref.sync import sync_to_async

添加網址:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import (
    index,
    async_view,
    sync_view,
    smoke_some_meats,
    burn_some_meats,
    async_with_sync_view
)


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("sync_to_async/", async_with_sync_view),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

在瀏覽器中測試它http://localhost:8000/sync_to_async/

在您的終端中,您應該看到:

INFO:     127.0.0.1:61365 - "GET /sync_to_async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

使用sync_to_async,阻塞同步調用在後台線程中處理,允許 HTTP 響應在第一次睡眠調用之前被發回。

Celery 和異步視圖

問: Django 的異步視圖仍然需要 Celery 嗎?

這取決於。

Django 的異步視圖提供了與任務或消息隊列類似的功能,但並不復雜。如果您正在使用(或正在考慮)Django 並且想做一些簡單的事情(並且不關心可靠性),那麼異步視圖是快速輕鬆地完成此任務的好方法。如果您需要執行更繁重、長時間運行的後台進程,您仍然需要使用 Celery 或 RQ。

應該注意的是,要有效地使用異步視圖,您應該只在視圖中進行異步調用。另一方面,任務隊列在單獨的進程上使用工作人員,因此能夠在後台、多台服務器上運行同步調用。

順便說一句,您不必在異步視圖和消息隊列之間進行選擇——您可以輕鬆地串聯使用它們。例如:您可以使用異步視圖發送電子郵件或進行一次性數據庫修改,但讓 Celery 在每晚的預定時間清理您的數據庫或生成並發送客戶報告。

何時使用

對於新建項目,如果您喜歡異步,請利用異步視圖並儘可能以異步方式編寫您的 I/O 流程。也就是說,如果您的大多數視圖只需要在返回數據之前調用數據庫並進行一些基本處理,那麼您不會看到與僅僅堅持同步視圖相比有很大的增加(如果有的話)。

對於棕地項目,如果您幾乎沒有 I/O 進程,請堅持使用同步視圖。如果您確實有許多 I/O 進程,請評估以異步方式重寫它們的難易程度。將同步 I/O 重寫為異步並不容易,因此您可能希望在嘗試重寫為異步之前優化同步 I/O 和視圖。另外,將同步進程與異步視圖混合絕不是一個好主意。

在生產中,一定要使用 Gunicorn 來管理 Uvicorn,以便同時利用並發性(通過 Uvicorn)和並行性(通過 Gunicorn worker):

gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application

結論

總之,雖然這是一個簡單的用例,但它應該讓您大致了解 Django 的異步視圖打開的可能性。在您的異步視圖中嘗試的其他一些事情是發送電子郵件、調用第三方 API 以及讀取/寫入文件。

來源:  https ://testdriven.io

#django #async 

如何開始使用 Django 的異步視圖

How to Get Started with Django's Asynchronous Views

Writing asynchronous code gives you the ability to speed up your application with little effort. Django versions >= 3.1 support async views, middleware, and tests. If you haven't already experimented with async views, now's a great time to get them under your belt.

This tutorial looks at how to get started with Django's asynchronous views.

Source: https://testdriven.io

#django #async 

How to Get Started with Django's Asynchronous Views
Noelia  Graham

Noelia Graham

1656842520

Comment Démarrer Avec Les Vues Asynchrones De Django

L'écriture de code asynchrone vous donne la possibilité d'accélérer votre application avec peu d'effort. Les versions de Django >= 3.1 prennent en charge les vues asynchrones, le middleware et les tests. Si vous n'avez pas encore expérimenté les vues asynchrones, c'est le moment idéal pour les mettre sous votre ceinture.

Ce didacticiel explique comment démarrer avec les vues asynchrones de Django.

Si vous souhaitez en savoir plus sur la puissance du code asynchrone ainsi que sur les différences entre les threads, le multitraitement et l'async en Python, consultez mon article Accélérer Python avec la concurrence, le parallélisme et l'asyncio .

Objectifs

À la fin de ce didacticiel, vous devriez être en mesure de :

  1. Écrire une vue asynchrone dans Django
  2. Faire une requête HTTP non bloquante dans une vue Django
  3. Simplifiez les tâches de base en arrière-plan avec les vues asynchrones de Django
  4. Utiliser sync_to_asyncpour effectuer un appel synchrone dans une vue asynchrone
  5. Expliquez quand vous devriez et ne devriez pas utiliser les vues asynchrones

Vous devez également être en mesure de répondre aux questions suivantes :

  1. Que se passe-t-il si vous effectuez un appel synchrone dans une vue asynchrone ?
  2. Que se passe-t-il si vous effectuez un appel synchrone et asynchrone dans une vue asynchrone ?
  3. Celery est-il toujours nécessaire avec les vues asynchrones de Django ?

Conditions préalables

Tant que vous êtes déjà familiarisé avec Django lui-même, l'ajout de fonctionnalités asynchrones à des vues non basées sur des classes est extrêmement simple.

Dépendances

  1. Python >= 3.10
  2. Django >= 4.0
  3. Uvicorne
  4. HTTPX

Qu'est-ce qu'ASGI ?

ASGI signifie Asynchronous Server Gateway Interface. Il s'agit de la suite moderne et asynchrone de WSGI , fournissant une norme pour la création d'applications Web asynchrones basées sur Python.

Une autre chose à mentionner est qu'ASGI est rétrocompatible avec WSGI, ce qui en fait une bonne excuse pour passer d'un serveur WSGI comme Gunicorn ou uWSGI à un serveur ASGI comme Uvicorn ou Daphne même si vous n'êtes pas prêt à passer à l'écriture d'applications asynchrones .

Création de l'application

Créez un nouveau répertoire de projet avec un nouveau projet Django :

$ mkdir django-async-views && cd django-async-views
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject hello_async .

N'hésitez pas à échanger virtualenv et Pip contre Poetry ou Pipenv . Pour en savoir plus, consultez Environnements Python modernes .

Django exécutera vos vues asynchrones si vous utilisez le serveur de développement intégré, mais il ne les exécutera pas réellement de manière asynchrone, nous allons donc exécuter Django avec Uvicorn.

Installez-le:

(env)$ pip install uvicorn

Pour exécuter votre projet avec Uvicorn, vous utilisez la commande suivante depuis la racine de votre projet :

uvicorn {name of your project}.asgi:application

Dans notre cas, ce serait :

(env)$ uvicorn hello_async.asgi:application

Ensuite, créons notre première vue asynchrone. Ajoutez un nouveau fichier pour contenir vos vues dans le dossier "hello_async", puis ajoutez la vue suivante :

# hello_async/views.py

from django.http import HttpResponse


async def index(request):
    return HttpResponse("Hello, async Django!")

La création de vues asynchrones dans Django est aussi simple que la création d'une vue synchrone -- tout ce que vous avez à faire est d'ajouter le mot- asyncclé.

Mettez à jour les URL :

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
]

Maintenant, dans un terminal, dans votre dossier racine, exécutez :

(env)$ uvicorn hello_async.asgi:application --reload

Le --reloaddrapeau indique à Uvicorn de surveiller vos fichiers pour les modifications et de recharger s'il en trouve. Cela allait probablement de soi.

Ouvrez http://localhost:8000/ dans votre navigateur Web préféré :

Hello, async Django!

Ce n'est pas la chose la plus excitante au monde, mais bon, c'est un début. Il convient de noter que l'exécution de cette vue avec un serveur de développement intégré à Django donnera exactement les mêmes fonctionnalités et résultats. C'est parce que nous ne faisons rien d'asynchrone dans le gestionnaire.

HTTPX

Il convient de noter que la prise en charge asynchrone est entièrement rétrocompatible, vous pouvez donc mélanger des vues asynchrones et synchronisées, des intergiciels et des tests. Django exécutera chacun dans le contexte d'exécution approprié.

Pour illustrer cela, ajoutez quelques nouvelles vues :

# hello_async/views.py

import asyncio
from time import sleep

import httpx
from django.http import HttpResponse


# helpers

async def http_call_async():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)


def http_call_sync():
    for num in range(1, 6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)


# views

async def index(request):
    return HttpResponse("Hello, async Django!")


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")


def sync_view(request):
    http_call_sync()
    return HttpResponse("Blocking HTTP request")

Mettez à jour les URL :

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view


urlpatterns = [
    path("admin/", admin.site.urls),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Installez HTTPX :

(env)$ pip install httpx

Avec le serveur en cours d'exécution, accédez à http://localhost:8000/async/ . Vous devriez immédiatement voir la réponse :

Non-blocking HTTP request

Dans votre terminal, vous devriez voir :

INFO:     127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Ici, la réponse HTTP est renvoyée avant le premier appel de veille.

Ensuite, accédez à http://localhost:8000/sync/ . Cela devrait prendre environ cinq secondes pour obtenir la réponse :

Blocking HTTP request

Tournez-vous vers le terminal :

1
2
3
4
5
<Response [200 OK]>
INFO:     127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK

Ici, la réponse HTTP est envoyée après la boucle et la requête se https://httpbin.org/termine.

Fumer des viandes

Pour simuler davantage un scénario réel de la façon dont vous tireriez parti de l'asynchronisme, examinons comment exécuter plusieurs opérations de manière asynchrone, agréger les résultats et les renvoyer à l'appelant.

De retour dans l'URLconf de votre projet, créez un nouveau chemin àsmoke_some_meats :

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

De retour dans vos vues, créez une nouvelle fonction d'assistance asynchrone appelée smoke. Cette fonction prend deux paramètres : une liste de chaînes appelée smokableset une chaîne appelée flavor. Ceux-ci sont par défaut une liste de viandes à fumer et de "Sweet Baby Ray's", respectivement.

# hello_async/views.py

async def smoke(smokables: List[str] = None, flavor: str = "Sweet Baby Ray's") -> List[str]:
    """ Smokes some meats and applies the Sweet Baby Ray's """

    for smokable in smokables:
        print(f"Smoking some {smokable}...")
        print(f"Applying the {flavor}...")
        print(f"{smokable.capitalize()} smoked.")

    return len(smokables)

La boucle for applique de manière asynchrone la saveur (lire : Sweet Baby Ray's) aux produits à fumer (lire : viandes fumées).

N'oubliez pas l'importation :

from typing import List

Listest utilisé pour des capacités de frappe supplémentaires. Ce n'est pas obligatoire et peut être facilement omis (nixez simplement la : List[str]déclaration de paramètre "smokables" suivante).

Ensuite, ajoutez deux autres assistants asynchrones :

async def get_smokables():
    print("Getting smokeables...")

    await asyncio.sleep(2)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning smokeable")
        return [
            "ribs",
            "brisket",
            "lemon chicken",
            "salmon",
            "bison sirloin",
            "sausage",
        ]


async def get_flavor():
    print("Getting flavor...")

    await asyncio.sleep(1)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning flavor")
        return random.choice(
            [
                "Sweet Baby Ray's",
                "Stubb's Original",
                "Famous Dave's",
            ]
        )

Assurez-vous d'ajouter l'importation :

import random

Créez la vue asynchrone qui utilise les fonctions asynchrones :

# hello_async/views.py

async def smoke_some_meats(request):
    results = await asyncio.gather(*[get_smokables(), get_flavor()])
    total = await asyncio.gather(*[smoke(results[0], results[1])])
    return HttpResponse(f"Smoked {total[0]} meats with {results[1]}!")

Cette vue appelle les fonctions get_smokableset get_flavorsimultanément. Étant donné que smokedépend des résultats de get_smokableset get_flavor, nous avions l'habitude gatherd'attendre la fin de chaque tâche asynchrone.

Gardez à l'esprit que, dans une vue de synchronisation régulière, get_smokablesils get_flavorseraient traités un à la fois. De plus, la vue asynchrone produira l'exécution et permettra de traiter d'autres demandes pendant que les tâches asynchrones sont traitées, ce qui permet de traiter plus de demandes par le même processus dans un laps de temps donné.

Enfin, une réponse est renvoyée pour informer l'utilisateur qu'un délicieux repas barbecue est prêt.

Super. Enregistrez le fichier, puis revenez à votre navigateur et accédez à http://localhost:8000/smoke_some_meats/ . Cela devrait prendre quelques secondes pour obtenir la réponse :

Smoked 6 meats with Sweet Baby Ray's!

Dans votre console, vous devriez voir :

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Smoking some ribs...
Applying the Stubb's Original...
Ribs smoked.
Smoking some brisket...
Applying the Stubb's Original...
Brisket smoked.
Smoking some lemon chicken...
Applying the Stubb's Original...
Lemon chicken smoked.
Smoking some salmon...
Applying the Stubb's Original...
Salmon smoked.
Smoking some bison sirloin...
Applying the Stubb's Original...
Bison sirloin smoked.
Smoking some sausage...
Applying the Stubb's Original...
Sausage smoked.
INFO:     127.0.0.1:57501 - "GET /smoke_some_meats/ HTTP/1.1" 200 OK

Prenez note de l'ordre des déclarations d'impression suivantes :

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

C'est l'asynchronicité au travail : pendant que la get_smokablesfonction dort, la get_flavorfonction termine le traitement.

Viandes brûlées

Appel synchronisé

Q : Que se passe-t-il si vous effectuez un appel synchrone dans une vue asynchrone ?

La même chose qui se produirait si vous appeliez une fonction non asynchrone à partir d'une vue non asynchrone.

--

Pour illustrer cela, créez une nouvelle fonction d'assistance dans votre views.py appeléeoversmoke :

# hello_async/views.py

def oversmoke() -> None:
    """ If it's not dry, it must be uncooked """
    sleep(5)
    print("Who doesn't love burnt meats?")

Très simple : nous attendons de manière synchrone cinq secondes.

Créez la vue qui appelle cette fonction :

# hello_async/views.py

async def burn_some_meats(request):
    oversmoke()
    return HttpResponse(f"Burned some meats.")

Enfin, câblez la route dans l'URLconf de votre projet :

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats, burn_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Visitez l'itinéraire dans le navigateur à http://localhost:8000/burn_some_meats :

Burned some meats.

Remarquez qu'il a fallu cinq secondes pour enfin obtenir une réponse du navigateur. Vous devriez également avoir reçu la sortie de la console en même temps :

Who doesn't love burnt meats?
INFO:     127.0.0.1:40682 - "GET /burn_some_meats HTTP/1.1" 200 OK

Il est peut-être intéressant de noter que la même chose se produira quel que soit le serveur que vous utilisez, qu'il soit basé sur WSGI ou ASGI.

Appels synchronisés et asynchrones

Q : Que se passe-t-il si vous effectuez un appel synchrone et un appel asynchrone dans une vue asynchrone ?

Ne fais pas ça.

Les vues synchrones et asynchrones ont tendance à mieux fonctionner à des fins différentes. Si vous avez une fonctionnalité de blocage dans une vue asynchrone, au mieux, ce ne sera pas mieux que d'utiliser simplement une vue synchrone.

Synchroniser avec asynchrone

Si vous devez effectuer un appel synchrone à l'intérieur d'une vue asynchrone (comme pour interagir avec la base de données via l'ORM de Django, par exemple), utilisez sync_to_async comme wrapper ou comme décorateur.

Exemple:

# hello_async/views.py

async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync, thread_sensitive=False)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request (via sync_to_async)")

Avez-vous remarqué que nous avons défini le thread_sensitiveparamètre sur False? Cela signifie que la fonction synchrone, http_call_sync, sera exécutée dans un nouveau thread. Consultez la documentation pour plus d'informations.

Ajoutez l'importation en haut :

from asgiref.sync import sync_to_async

Ajoutez l'URL :

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import (
    index,
    async_view,
    sync_view,
    smoke_some_meats,
    burn_some_meats,
    async_with_sync_view
)


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("sync_to_async/", async_with_sync_view),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Testez-le dans votre navigateur à http://localhost:8000/sync_to_async/ .

Dans votre terminal, vous devriez voir :

INFO:     127.0.0.1:61365 - "GET /sync_to_async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

À l' aide sync_to_asyncde , l'appel synchrone bloquant était traité dans un thread d'arrière-plan, permettant à la réponse HTTP d'être renvoyée avant le premier appel de mise en veille.

Céleri et vues asynchrones

Q : Le céleri est-il toujours nécessaire avec les vues asynchrones de Django ?

Ça dépend.

Les vues asynchrones de Django offrent des fonctionnalités similaires à une file d'attente de tâches ou de messages sans la complexité. Si vous utilisez (ou envisagez) Django et que vous voulez faire quelque chose de simple (et que vous ne vous souciez pas de la fiabilité), les vues asynchrones sont un excellent moyen d'accomplir cela rapidement et facilement. Si vous devez effectuer des processus d'arrière-plan beaucoup plus lourds et de longue durée, vous voudrez toujours utiliser Celery ou RQ.

Il convient de noter que pour utiliser efficacement les vues asynchrones, vous ne devez avoir que des appels asynchrones dans la vue. Les files d'attente de tâches, en revanche, utilisent des travailleurs sur des processus distincts et sont donc capables d'exécuter des appels synchrones en arrière-plan, sur plusieurs serveurs.

Soit dit en passant, vous ne devez en aucun cas choisir entre des vues asynchrones et une file d'attente de messages - vous pouvez facilement les utiliser en tandem. Par exemple : vous pouvez utiliser une vue asynchrone pour envoyer un e-mail ou effectuer une modification ponctuelle de la base de données, mais demander à Celery de nettoyer votre base de données à une heure programmée chaque nuit ou de générer et d'envoyer des rapports client.

Quand utiliser

Pour les nouveaux projets, si l'asynchrone est votre truc, tirez parti des vues asynchrones et écrivez vos processus d'E/S de manière asynchrone autant que possible. Cela dit, si la plupart de vos vues ont juste besoin d'appeler une base de données et d'effectuer un traitement de base avant de renvoyer les données, vous ne verrez pas beaucoup d'augmentation (le cas échéant) par rapport aux vues synchronisées.

Pour les projets brownfield, si vous avez peu ou pas de processus d'E/S, restez fidèle aux vues de synchronisation. Si vous avez un certain nombre de processus d'E/S, évaluez à quel point il sera facile de les réécrire de manière asynchrone. Réécrire les E/S de synchronisation en asynchrone n'est pas facile, vous voudrez donc probablement optimiser vos E/S de synchronisation et vos vues avant d'essayer de réécrire en asynchrone. De plus, ce n'est jamais une bonne idée de mélanger les processus de synchronisation avec vos vues asynchrones.

En production, assurez-vous d'utiliser Gunicorn pour gérer Uvicorn afin de tirer parti à la fois de la concurrence (via Uvicorn) et du parallélisme (via les travailleurs Gunicorn) :

gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application

Conclusion

En conclusion, bien qu'il s'agisse d'un cas d'utilisation simple, cela devrait vous donner une idée approximative des possibilités qu'ouvrent les vues asynchrones de Django. D'autres choses à essayer dans vos vues asynchrones sont l'envoi d'e-mails, l'appel d'API tierces et la lecture/écriture de fichiers.

Source:  https://testdrive.io

#django #async 

Comment Démarrer Avec Les Vues Asynchrones De Django

Cómo Comenzar Con Las Vistas Asíncronas De Django

Escribir código asíncrono le brinda la capacidad de acelerar su aplicación con poco esfuerzo. Las versiones de Django >= 3.1 admiten vistas asíncronas, middleware y pruebas. Si aún no ha experimentado con vistas asíncronas, ahora es un buen momento para obtenerlas en su haber.

Este tutorial analiza cómo comenzar con las vistas asíncronas de Django.

Si está interesado en obtener más información sobre el poder detrás del código asíncrono junto con las diferencias entre subprocesos, multiprocesamiento y asíncrono en Python, consulte mi artículo Acelerando Python con concurrencia, paralelismo y asyncio .

Objetivos

Al final de este tutorial, debería ser capaz de:

  1. Escribir una vista asíncrona en Django
  2. Realice una solicitud HTTP sin bloqueo en una vista de Django
  3. Simplifique las tareas básicas en segundo plano con las vistas asíncronas de Django
  4. Úselo sync_to_asyncpara realizar una llamada síncrona dentro de una vista asíncrona
  5. Explique cuándo debe y no debe usar vistas asíncronas

También debe ser capaz de responder a las siguientes preguntas:

  1. ¿Qué sucede si realiza una llamada síncrona dentro de una vista asíncrona?
  2. ¿Qué sucede si realiza una llamada síncrona y asíncrona dentro de una vista asíncrona?
  3. ¿Sigue siendo necesario Celery con las vistas asíncronas de Django?

requisitos previos

Siempre que esté familiarizado con Django, agregar funcionalidad asíncrona a vistas no basadas en clases es extremadamente sencillo.

dependencias

  1. Pitón >= 3.10
  2. Django >= 4.0
  3. uvicornio
  4. HTTPX

¿Qué es ASGI?

ASGI significa Interfaz de puerta de enlace de servidor asíncrono. Es el seguimiento moderno y asíncrono de WSGI , que proporciona un estándar para crear aplicaciones web asíncronas basadas en Python.

Otra cosa que vale la pena mencionar es que ASGI es compatible con versiones anteriores de WSGI, lo que lo convierte en una buena excusa para cambiar de un servidor WSGI como Gunicorn o uWSGI a un servidor ASGI como Uvicorn o Daphne , incluso si no está listo para cambiar a escribir aplicaciones asincrónicas. .

Crear la aplicación

Cree un nuevo directorio de proyectos junto con un nuevo proyecto de Django:

$ mkdir django-async-views && cd django-async-views
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject hello_async .

Siéntete libre de cambiar virtualenv y Pip por Poetry o Pipenv . Para obtener más información, revise Entornos modernos de Python .

Django ejecutará sus vistas asíncronas si está utilizando el servidor de desarrollo integrado, pero en realidad no las ejecutará de forma asíncrona, por lo que ejecutaremos Django con Uvicorn.

Instalarlo:

(env)$ pip install uvicorn

Para ejecutar su proyecto con Uvicorn, use el siguiente comando desde la raíz de su proyecto:

uvicorn {name of your project}.asgi:application

En nuestro caso, esto sería:

(env)$ uvicorn hello_async.asgi:application

A continuación, creemos nuestra primera vista asíncrona. Agregue un nuevo archivo para mantener sus vistas en la carpeta "hello_async" y luego agregue la siguiente vista:

# hello_async/views.py

from django.http import HttpResponse


async def index(request):
    return HttpResponse("Hello, async Django!")

Crear vistas asíncronas en Django es tan simple como crear una vista síncrona: todo lo que necesita hacer es agregar la asyncpalabra clave.

Actualice las URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
]

Ahora, en una terminal, en su carpeta raíz, ejecute:

(env)$ uvicorn hello_async.asgi:application --reload

La --reloadbandera le dice a Uvicorn que mire sus archivos en busca de cambios y que los vuelva a cargar si encuentra alguno. Eso probablemente se explica por sí mismo.

Abra http://localhost:8000/ en su navegador web favorito:

Hello, async Django!

No es lo más emocionante del mundo, pero bueno, es un comienzo. Vale la pena señalar que ejecutar esta vista con un servidor de desarrollo incorporado de Django dará como resultado exactamente la misma funcionalidad y salida. Esto se debe a que en realidad no estamos haciendo nada asíncrono en el controlador.

HTTPX

Vale la pena señalar que el soporte asíncrono es totalmente compatible con versiones anteriores, por lo que puede combinar vistas asíncronas y sincronizadas, middleware y pruebas. Django ejecutará cada uno en el contexto de ejecución adecuado.

Para demostrar esto, agregue algunas vistas nuevas:

# hello_async/views.py

import asyncio
from time import sleep

import httpx
from django.http import HttpResponse


# helpers

async def http_call_async():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)


def http_call_sync():
    for num in range(1, 6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)


# views

async def index(request):
    return HttpResponse("Hello, async Django!")


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")


def sync_view(request):
    http_call_sync()
    return HttpResponse("Blocking HTTP request")

Actualice las URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view


urlpatterns = [
    path("admin/", admin.site.urls),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Instalar HTTPX :

(env)$ pip install httpx

Con el servidor en ejecución, vaya a http://localhost:8000/async/ . Inmediatamente debería ver la respuesta:

Non-blocking HTTP request

En tu terminal deberías ver:

INFO:     127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Aquí, la respuesta HTTP se devuelve antes de la primera llamada de suspensión.

A continuación, navegue hasta http://localhost:8000/sync/ . Debería tomar alrededor de cinco segundos obtener la respuesta:

Blocking HTTP request

Gire a la terminal:

1
2
3
4
5
<Response [200 OK]>
INFO:     127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK

Aquí, la respuesta HTTP se envía después del ciclo y la solicitud se https://httpbin.org/completa.

Fumar algunas carnes

Para simular más un escenario del mundo real de cómo aprovecharía la sincronización, veamos cómo ejecutar varias operaciones de forma asíncrona, agregar los resultados y devolverlos a la persona que llama.

De vuelta en la URLconf de su proyecto, cree una nueva ruta en smoke_some_meats:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

De vuelta en sus vistas, cree una nueva función auxiliar asincrónica llamada smoke. Esta función toma dos parámetros: una lista de cadenas llamadas smokablesy una cadena llamada flavor. Estos por defecto son una lista de carnes fumables y "Sweet Baby Ray's", respectivamente.

# hello_async/views.py

async def smoke(smokables: List[str] = None, flavor: str = "Sweet Baby Ray's") -> List[str]:
    """ Smokes some meats and applies the Sweet Baby Ray's """

    for smokable in smokables:
        print(f"Smoking some {smokable}...")
        print(f"Applying the {flavor}...")
        print(f"{smokable.capitalize()} smoked.")

    return len(smokables)

El ciclo for aplica asincrónicamente el sabor (léase: Sweet Baby Ray's) a los ahumables (léase: carnes ahumadas).

No olvides la importación:

from typing import List

Listse utiliza para capacidades adicionales de escritura. Esto no es obligatorio y se puede omitir fácilmente (simplemente elimine la : List[str]siguiente declaración del parámetro "smokables").

A continuación, agregue dos ayudantes asíncronos más:

async def get_smokables():
    print("Getting smokeables...")

    await asyncio.sleep(2)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning smokeable")
        return [
            "ribs",
            "brisket",
            "lemon chicken",
            "salmon",
            "bison sirloin",
            "sausage",
        ]


async def get_flavor():
    print("Getting flavor...")

    await asyncio.sleep(1)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning flavor")
        return random.choice(
            [
                "Sweet Baby Ray's",
                "Stubb's Original",
                "Famous Dave's",
            ]
        )

Asegúrate de agregar la importación:

import random

Cree la vista asíncrona que usa las funciones asíncronas:

# hello_async/views.py

async def smoke_some_meats(request):
    results = await asyncio.gather(*[get_smokables(), get_flavor()])
    total = await asyncio.gather(*[smoke(results[0], results[1])])
    return HttpResponse(f"Smoked {total[0]} meats with {results[1]}!")

Esta vista llama a las funciones get_smokablesy al get_flavormismo tiempo. Dado smokeque depende de los resultados de ambos get_smokablesy get_flavor, solíamos gatheresperar a que se completara cada tarea asíncrona.

Tenga en cuenta que en una vista de sincronización regular, get_smokablesy get_flavorse manejaría uno a la vez. Además, la vista asincrónica producirá la ejecución y permitirá que se procesen otras solicitudes mientras se procesan las tareas asincrónicas, lo que permite que el mismo proceso maneje más solicitudes en un período de tiempo determinado.

Finalmente, se devuelve una respuesta para informar al usuario que su deliciosa comida BBQ está lista.

Excelente. Guarde el archivo, luego regrese a su navegador y navegue hasta http://localhost:8000/smoke_some_meats/ . Debería tomar unos segundos obtener la respuesta:

Smoked 6 meats with Sweet Baby Ray's!

En su consola, debería ver:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Smoking some ribs...
Applying the Stubb's Original...
Ribs smoked.
Smoking some brisket...
Applying the Stubb's Original...
Brisket smoked.
Smoking some lemon chicken...
Applying the Stubb's Original...
Lemon chicken smoked.
Smoking some salmon...
Applying the Stubb's Original...
Salmon smoked.
Smoking some bison sirloin...
Applying the Stubb's Original...
Bison sirloin smoked.
Smoking some sausage...
Applying the Stubb's Original...
Sausage smoked.
INFO:     127.0.0.1:57501 - "GET /smoke_some_meats/ HTTP/1.1" 200 OK

Tome nota del orden de las siguientes declaraciones impresas:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Esta es la asincronía en el trabajo: mientras la get_smokablesfunción duerme, la get_flavorfunción termina de procesar.

Carnes Quemadas

sincronizar llamada

P: ¿Qué sucede si realiza una llamada síncrona dentro de una vista asíncrona?

Lo mismo que sucedería si llamara a una función no asíncrona desde una vista no asíncrona.

--

Para ilustrar esto, cree una nueva función de ayuda en sus vistas.py llamada oversmoke:

# hello_async/views.py

def oversmoke() -> None:
    """ If it's not dry, it must be uncooked """
    sleep(5)
    print("Who doesn't love burnt meats?")

Muy sencillo: solo estamos esperando sincrónicamente durante cinco segundos.

Cree la vista que llama a esta función:

# hello_async/views.py

async def burn_some_meats(request):
    oversmoke()
    return HttpResponse(f"Burned some meats.")

Por último, conecte la ruta en la URLconf de su proyecto:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats, burn_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Visite la ruta en el navegador en http://localhost:8000/burn_some_meats :

Burned some meats.

Observe cómo tardó cinco segundos en obtener finalmente una respuesta del navegador. También debería haber recibido la salida de la consola al mismo tiempo:

Who doesn't love burnt meats?
INFO:     127.0.0.1:40682 - "GET /burn_some_meats HTTP/1.1" 200 OK

Posiblemente valga la pena señalar que sucederá lo mismo independientemente del servidor que esté utilizando, ya sea basado en WSGI o ASGI.

Llamadas sincronizadas y asíncronas

P: ¿Qué sucede si realiza una llamada síncrona y asíncrona dentro de una vista asíncrona?

No hagas esto.

Las vistas sincrónicas y asincrónicas tienden a funcionar mejor para diferentes propósitos. Si tiene la funcionalidad de bloqueo en una vista asíncrona, en el mejor de los casos no será mejor que simplemente usar una vista síncrona.

Sincronizar a asíncrono

Si necesita realizar una llamada síncrona dentro de una vista asíncrona (como interactuar con la base de datos a través de Django ORM, por ejemplo), use sync_to_async ya sea como contenedor o decorador.

Ejemplo:

# hello_async/views.py

async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync, thread_sensitive=False)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request (via sync_to_async)")

¿Te diste cuenta de que configuramos el thread_sensitiveparámetro en False? Esto significa que la función síncrona http_call_sync, se ejecutará en un nuevo subproceso. Revise los documentos para obtener más información.

Agregue la importación a la parte superior:

from asgiref.sync import sync_to_async

Agregue la URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import (
    index,
    async_view,
    sync_view,
    smoke_some_meats,
    burn_some_meats,
    async_with_sync_view
)


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("sync_to_async/", async_with_sync_view),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Pruébelo en su navegador en http://localhost:8000/sync_to_async/ .

En tu terminal deberías ver:

INFO:     127.0.0.1:61365 - "GET /sync_to_async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Con sync_to_async, la llamada síncrona de bloqueo se procesó en un subproceso en segundo plano, lo que permitió que la respuesta HTTP se devolviera antes de la primera llamada de suspensión.

Vistas de apio y asíncronas

P: ¿Sigue siendo necesario Celery con las vistas asíncronas de Django?

Eso depende.

Las vistas asíncronas de Django ofrecen una funcionalidad similar a una tarea o cola de mensajes sin la complejidad. Si está usando (o está considerando) Django y quiere hacer algo simple (y no le importa la confiabilidad), las vistas asíncronas son una excelente manera de lograr esto de manera rápida y sencilla. Si necesita realizar procesos en segundo plano mucho más pesados ​​​​y de larga duración, aún querrá usar Celery o RQ.

Cabe señalar que para usar las vistas asíncronas de manera efectiva, solo debe tener llamadas asíncronas en la vista. Las colas de tareas, por otro lado, usan trabajadores en procesos separados y, por lo tanto, son capaces de ejecutar llamadas síncronas en segundo plano, en múltiples servidores.

Por cierto, de ninguna manera debe elegir entre vistas asíncronas y una cola de mensajes; puede usarlas fácilmente en conjunto. Por ejemplo: podría usar una vista asíncrona para enviar un correo electrónico o hacer una modificación única de la base de datos, pero hacer que Celery limpie su base de datos a una hora programada todas las noches o generar y enviar informes de clientes.

Cuándo usar

Para proyectos totalmente nuevos, si lo suyo es asincrónico, aproveche las vistas asincrónicas y escriba sus procesos de E/S de la forma más asincrónica posible. Dicho esto, si la mayoría de sus vistas solo necesitan hacer llamadas a una base de datos y hacer un procesamiento básico antes de devolver los datos, no verá un gran aumento (si lo hay) en comparación con las vistas sincronizadas.

Para proyectos brownfield, si tiene pocos o ningún proceso de E/S, quédese con las vistas sincronizadas. Si tiene varios procesos de E/S, calcule lo fácil que será reescribirlos de forma asíncrona. Reescribir la E/S sincronizada a asíncrona no es fácil, por lo que probablemente querrá optimizar su E/S sincronizada y sus vistas antes de intentar reescribir a asíncrona. Además, nunca es una buena idea mezclar procesos de sincronización con sus vistas asíncronas.

En producción, asegúrese de usar Gunicorn para administrar Uvicorn con el fin de aprovechar tanto la simultaneidad (a través de Uvicorn) como el paralelismo (a través de los trabajadores de Gunicorn):

gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application

Conclusión

En conclusión, aunque este fue un caso de uso simple, debería darle una idea aproximada de las posibilidades que abren las vistas asincrónicas de Django. Algunas otras cosas que puede probar en sus vistas asíncronas son enviar correos electrónicos, llamar a API de terceros y leer o escribir en archivos.

Fuente:  https://testdriven.io

#django #async 

Cómo Comenzar Con Las Vistas Asíncronas De Django
Neil  Morgan

Neil Morgan

1656831540

Como Começar Com As Visualizações Assíncronas Do Django

Escrever código assíncrono oferece a capacidade de acelerar seu aplicativo com pouco esforço. As versões do Django >= 3.1 suportam visualizações assíncronas, middleware e testes. Se você ainda não experimentou visualizações assíncronas, agora é um ótimo momento para colocá-las em prática.

Este tutorial mostra como começar com as visualizações assíncronas do Django.

Se você estiver interessado em aprender mais sobre o poder por trás do código assíncrono, juntamente com as diferenças entre threads, multiprocessamento e assíncrono em Python, confira meu artigo Acelerando o Python com simultaneidade, paralelismo e assíncrono .

Objetivos

Ao final deste tutorial, você deverá ser capaz de:

  1. Escreva uma visão assíncrona no Django
  2. Faça uma solicitação HTTP sem bloqueio em uma visualização do Django
  3. Simplifique as tarefas básicas em segundo plano com as visualizações assíncronas do Django
  4. Use sync_to_asyncpara fazer uma chamada síncrona dentro de uma visualização assíncrona
  5. Explique quando você deve e não deve usar visualizações assíncronas

Você também deve ser capaz de responder às seguintes perguntas:

  1. E se você fizer uma chamada síncrona dentro de uma visualização assíncrona?
  2. E se você fizer uma chamada síncrona e assíncrona dentro de uma visualização assíncrona?
  3. O aipo ainda é necessário com as visualizações assíncronas do Django?

Pré-requisitos

Contanto que você já esteja familiarizado com o próprio Django, adicionar funcionalidade assíncrona a visualizações não baseadas em classe é extremamente simples.

Dependências

  1. Python >= 3,10
  2. Django >= 4.0
  3. Uvicorn
  4. HTTPX

O que é ASGI?

ASGI significa Interface de Gateway de Servidor Assíncrono. É a continuação moderna e assíncrona do WSGI , fornecendo um padrão para a criação de aplicativos Web assíncronos baseados em Python.

Outra coisa que vale a pena mencionar é que ASGI é compatível com WSGI, tornando-se uma boa desculpa para mudar de um servidor WSGI como Gunicorn ou uWSGI para um servidor ASGI como Uvicorn ou Daphne , mesmo se você não estiver pronto para mudar para escrever aplicativos assíncronos .

Criando o aplicativo

Crie um novo diretório de projeto junto com um novo projeto Django:

$ mkdir django-async-views && cd django-async-views
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject hello_async .

Sinta-se à vontade para trocar virtualenv e Pip por Poetry ou Pipenv . Para saber mais, revise Ambientes Python Modernos .

O Django executará suas visualizações assíncronas se você estiver usando o servidor de desenvolvimento integrado, mas na verdade não as executará de forma assíncrona, então executaremos o Django com o Uvicorn.

Instale-o:

(env)$ pip install uvicorn

Para executar seu projeto com o Uvicorn, você usa o seguinte comando da raiz do seu projeto:

uvicorn {name of your project}.asgi:application

No nosso caso seria:

(env)$ uvicorn hello_async.asgi:application

Em seguida, vamos criar nossa primeira visualização assíncrona. Adicione um novo arquivo para manter suas visualizações na pasta "hello_async" e adicione a seguinte visualização:

# hello_async/views.py

from django.http import HttpResponse


async def index(request):
    return HttpResponse("Hello, async Django!")

Criar visualizações assíncronas no Django é tão simples quanto criar uma visualização síncrona -- tudo que você precisa fazer é adicionar a palavra- asyncchave.

Atualize os URLs:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index


urlpatterns = [
    path("admin/", admin.site.urls),
    path("", index),
]

Agora, em um terminal, na sua pasta raiz, execute:

(env)$ uvicorn hello_async.asgi:application --reload

O --reloadsinalizador diz ao Uvicorn para observar seus arquivos em busca de alterações e recarregar se encontrar algum. Isso foi provavelmente auto-explicativo.

Abra http://localhost:8000/ em seu navegador favorito:

Hello, async Django!

Não é a coisa mais excitante do mundo, mas, ei, é um começo. Vale a pena notar que executar esta visão com um servidor de desenvolvimento embutido do Django resultará exatamente na mesma funcionalidade e saída. Isso ocorre porque, na verdade, não estamos fazendo nada assíncrono no manipulador.

HTTPX

Vale a pena notar que o suporte assíncrono é totalmente compatível com versões anteriores, para que você possa misturar exibições assíncronas e sincronizadas, middleware e testes. O Django executará cada um no contexto de execução apropriado.

Para demonstrar isso, adicione algumas novas visualizações:

# hello_async/views.py

import asyncio
from time import sleep

import httpx
from django.http import HttpResponse


# helpers

async def http_call_async():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)


def http_call_sync():
    for num in range(1, 6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)


# views

async def index(request):
    return HttpResponse("Hello, async Django!")


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")


def sync_view(request):
    http_call_sync()
    return HttpResponse("Blocking HTTP request")

Atualize os URLs:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view


urlpatterns = [
    path("admin/", admin.site.urls),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Instale o HTTPX :

(env)$ pip install httpx

Com o servidor em execução, navegue até http://localhost:8000/async/ . Você deve ver imediatamente a resposta:

Non-blocking HTTP request

No seu terminal você deverá ver:

INFO:     127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Aqui, a resposta HTTP é enviada de volta antes da primeira chamada de suspensão.

Em seguida, navegue até http://localhost:8000/sync/ . Deve levar cerca de cinco segundos para obter a resposta:

Blocking HTTP request

Ligue para o terminal:

1
2
3
4
5
<Response [200 OK]>
INFO:     127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK

Aqui, a resposta HTTP é enviada após o loop e a solicitação é https://httpbin.org/concluída.

Fumar algumas carnes

Para simular mais um cenário do mundo real de como você aproveitaria a assíncrona, vejamos como executar várias operações de forma assíncrona, agregar os resultados e devolvê-los ao chamador.

De volta ao URLconf do seu projeto, crie um novo caminho em smoke_some_meats:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

De volta às suas visualizações, crie uma nova função auxiliar assíncrona chamada smoke. Esta função recebe dois parâmetros: uma lista de strings chamadas smokablese uma string chamada flavor. Estes padrões para uma lista de carnes defumadas e "Sweet Baby Ray's", respectivamente.

# hello_async/views.py

async def smoke(smokables: List[str] = None, flavor: str = "Sweet Baby Ray's") -> List[str]:
    """ Smokes some meats and applies the Sweet Baby Ray's """

    for smokable in smokables:
        print(f"Smoking some {smokable}...")
        print(f"Applying the {flavor}...")
        print(f"{smokable.capitalize()} smoked.")

    return len(smokables)

O loop for aplica o sabor de forma assíncrona (leia-se: Sweet Baby Ray's) aos fumáveis ​​(leia-se: carnes defumadas).

Não esqueça da importação:

from typing import List

Listé usado para recursos extras de digitação. Isso não é necessário e pode ser facilmente omitido (apenas nix a : List[str]seguir a declaração do parâmetro "smokables").

Em seguida, adicione mais dois auxiliares assíncronos:

async def get_smokables():
    print("Getting smokeables...")

    await asyncio.sleep(2)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning smokeable")
        return [
            "ribs",
            "brisket",
            "lemon chicken",
            "salmon",
            "bison sirloin",
            "sausage",
        ]


async def get_flavor():
    print("Getting flavor...")

    await asyncio.sleep(1)
    async with httpx.AsyncClient() as client:
        await client.get("https://httpbin.org/")

        print("Returning flavor")
        return random.choice(
            [
                "Sweet Baby Ray's",
                "Stubb's Original",
                "Famous Dave's",
            ]
        )

Certifique-se de adicionar a importação:

import random

Crie a visualização assíncrona que usa as funções assíncronas:

# hello_async/views.py

async def smoke_some_meats(request):
    results = await asyncio.gather(*[get_smokables(), get_flavor()])
    total = await asyncio.gather(*[smoke(results[0], results[1])])
    return HttpResponse(f"Smoked {total[0]} meats with {results[1]}!")

Essa exibição chama as funções get_smokablese get_flavorsimultaneamente. Como smokedepende dos resultados de get_smokablese get_flavor, costumávamos gatheraguardar a conclusão de cada tarefa assíncrona.

Lembre-se de que, em uma exibição de sincronização regular, get_smokablesseria get_flavortratado um de cada vez. Além disso, a visualização assíncrona produzirá a execução e permitirá que outras solicitações sejam processadas enquanto as tarefas assíncronas são processadas, o que permite que mais solicitações sejam tratadas pelo mesmo processo em um determinado período de tempo.

Por fim, uma resposta é retornada para informar ao usuário que sua deliciosa refeição de churrasco está pronta.

Excelente. Salve o arquivo, volte ao seu navegador e navegue até http://localhost:8000/smoke_some_meats/ . Deve levar alguns segundos para obter a resposta:

Smoked 6 meats with Sweet Baby Ray's!

Em seu console, você deverá ver:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Smoking some ribs...
Applying the Stubb's Original...
Ribs smoked.
Smoking some brisket...
Applying the Stubb's Original...
Brisket smoked.
Smoking some lemon chicken...
Applying the Stubb's Original...
Lemon chicken smoked.
Smoking some salmon...
Applying the Stubb's Original...
Salmon smoked.
Smoking some bison sirloin...
Applying the Stubb's Original...
Bison sirloin smoked.
Smoking some sausage...
Applying the Stubb's Original...
Sausage smoked.
INFO:     127.0.0.1:57501 - "GET /smoke_some_meats/ HTTP/1.1" 200 OK

Observe a ordem das seguintes instruções de impressão:

Getting smokeables...
Getting flavor...
Returning flavor
Returning smokeable

Isso é assincronicidade no trabalho: enquanto a get_smokablesfunção dorme, a get_flavorfunção termina o processamento.

Carnes Queimadas

Sincronizar chamada

P: E se você fizer uma chamada síncrona dentro de uma visualização assíncrona?

A mesma coisa que aconteceria se você chamasse uma função não assíncrona de uma exibição não assíncrona.

--

Para ilustrar isso, crie uma nova função auxiliar em seu views.py chamada oversmoke:

# hello_async/views.py

def oversmoke() -> None:
    """ If it's not dry, it must be uncooked """
    sleep(5)
    print("Who doesn't love burnt meats?")

Muito simples: estamos apenas aguardando de forma síncrona por cinco segundos.

Crie a view que chama esta função:

# hello_async/views.py

async def burn_some_meats(request):
    oversmoke()
    return HttpResponse(f"Burned some meats.")

Por fim, conecte a rota no URLconf do seu projeto:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import index, async_view, sync_view, smoke_some_meats, burn_some_meats


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Visite a rota no navegador em http://localhost:8000/burn_some_meats :

Burned some meats.

Observe como levou cinco segundos para finalmente obter uma resposta do navegador. Você também deve ter recebido a saída do console ao mesmo tempo:

Who doesn't love burnt meats?
INFO:     127.0.0.1:40682 - "GET /burn_some_meats HTTP/1.1" 200 OK

Possivelmente vale a pena notar que a mesma coisa acontecerá independentemente do servidor que você estiver usando, seja ele baseado em WSGI ou ASGI.

Chamadas sincronizadas e assíncronas

P: E se você fizer uma chamada síncrona e assíncrona dentro de uma visualização assíncrona?

Não faça isso.

Visualizações síncronas e assíncronas tendem a funcionar melhor para diferentes propósitos. Se você tiver a funcionalidade de bloqueio em uma exibição assíncrona, na melhor das hipóteses não será melhor do que apenas usar uma exibição síncrona.

Sincronizar com assíncrono

Se você precisar fazer uma chamada síncrona dentro de uma visão assíncrona (como interagir com o banco de dados através do Django ORM, por exemplo), use sync_to_async como wrapper ou decorador.

Exemplo:

# hello_async/views.py

async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync, thread_sensitive=False)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request (via sync_to_async)")

Você notou que definimos o thread_sensitiveparâmetro para False? Isso significa que a função síncrona, http_call_sync, será executada em um novo thread. Revise os documentos para obter mais informações.

Adicione a importação ao topo:

from asgiref.sync import sync_to_async

Adicione o URL:

# hello_async/urls.py

from django.contrib import admin
from django.urls import path

from hello_async.views import (
    index,
    async_view,
    sync_view,
    smoke_some_meats,
    burn_some_meats,
    async_with_sync_view
)


urlpatterns = [
    path("admin/", admin.site.urls),
    path("smoke_some_meats/", smoke_some_meats),
    path("burn_some_meats/", burn_some_meats),
    path("sync_to_async/", async_with_sync_view),
    path("async/", async_view),
    path("sync/", sync_view),
    path("", index),
]

Teste-o em seu navegador em http://localhost:8000/sync_to_async/ .

No seu terminal você deverá ver:

INFO:     127.0.0.1:61365 - "GET /sync_to_async/ HTTP/1.1" 200 OK
1
2
3
4
5
<Response [200 OK]>

Usando sync_to_async, a chamada síncrona de bloqueio foi processada em um thread em segundo plano, permitindo que a resposta HTTP fosse enviada de volta antes da primeira chamada de suspensão.

Aipo e visualizações assíncronas

P: O Celery ainda é necessário com as visualizações assíncronas do Django?

Depende.

As visualizações assíncronas do Django oferecem funcionalidade semelhante a uma fila de tarefas ou mensagens sem a complexidade. Se você está usando (ou está considerando) o Django e quer fazer algo simples (e não se importa com confiabilidade), as visualizações assíncronas são uma ótima maneira de fazer isso de forma rápida e fácil. Se você precisar executar processos em segundo plano muito mais pesados ​​e de longa duração, ainda precisará usar o Celery ou o RQ.

Deve-se observar que, para usar exibições assíncronas de maneira eficaz, você deve ter apenas chamadas assíncronas na exibição. As filas de tarefas, por outro lado, usam trabalhadores em processos separados e, portanto, são capazes de executar chamadas síncronas em segundo plano, em vários servidores.

A propósito, você não deve escolher entre exibições assíncronas e uma fila de mensagens - você pode usá-las facilmente em conjunto. Por exemplo: você pode usar uma exibição assíncrona para enviar um email ou fazer uma modificação única no banco de dados, mas fazer com que o Celery limpe seu banco de dados em um horário agendado todas as noites ou gere e envie relatórios de clientes.

Quando usar

Para projetos greenfield, se você gosta de assíncrona, aproveite as visualizações assíncronas e escreva seus processos de E/S de maneira assíncrona o máximo possível. Dito isso, se a maioria de suas visualizações precisar apenas fazer chamadas para um banco de dados e fazer algum processamento básico antes de retornar os dados, você não verá muito aumento (se houver) sobre apenas manter as visualizações de sincronização.

Para projetos brownfield, se você tiver pouco ou nenhum processo de E/S, fique com as visualizações de sincronização. Se você tiver vários processos de E/S, avalie como será fácil reescrevê-los de maneira assíncrona. Reescrever a E/S de sincronização para assíncrona não é fácil, portanto, você provavelmente desejará otimizar sua E/S de sincronização e exibições antes de tentar reescrever para assíncrona. Além disso, nunca é uma boa ideia misturar processos de sincronização com suas visualizações assíncronas.

Na produção, certifique-se de usar o Gunicorn para gerenciar o Uvicorn para aproveitar a simultaneidade (via Uvicorn) e o paralelismo (via trabalhadores do Gunicorn):

gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application

Conclusão

Concluindo, embora este seja um caso de uso simples, ele deve lhe dar uma ideia aproximada das possibilidades que as visões assíncronas do Django abrem. Algumas outras coisas para tentar em suas visualizações assíncronas são enviar e-mails, chamar APIs de terceiros e ler/gravar em arquivos.

Fonte:  https://testdrive.io

#django #async 

Como Começar Com As Visualizações Assíncronas Do Django
Grace  Edwards

Grace Edwards

1656435060

Tips for Django Optimization Technique !

This video is all about the Django optimisation technique. In this video, we are going to share tips that you can use to boost your web application and serve the customers at a faster pace. In recent times, developers are creating a lot of web-based applications, and Django is one of the most preferred python-based frameworks. Django is straightforward to use and flexible. This flexibility slows down the application.

#django #python 

Tips for Django Optimization Technique !
Python  Library

Python Library

1656394440

Nsupdate: Dynamic DNS Service Written in Python

About nsupdate.info

nsupdate.info is also the name of the software used to implement it. If you like, you can use it to host the service on your own server.

Features

  • Frontend: Dynamic DNS updates via dyndns2 protocol (like supported by many DSL/cable routers and client software).
  • Backends:
    • Uses Dynamic DNS UPDATE protocol (RFC 2136) to update compatible nameservers like BIND, PowerDNS and others (the nameserver itself is not included).
    • Optionally uses the dyndns2 protocol to update other services - we can send updates to configurable third-party services when we receive an update from the router / update client.
  • Prominently shows visitor's IP addresses (v4 and v6) on main view, shows reverse DNS lookup results (on host overview view).
  • Multiple Hosts per user (using separate secrets for security)
  • Add own domains / nameservers (public or only for yourself)
  • Related Hosts: support updating DNS records of other hosts in same LAN by a single updater (e.g. for IPv6 with changing prefix, IPv4 also works)
  • Login with local or remote accounts (Google, GitHub, Bitbucket, ... accounts - everything supported by the python-social-auth package)
  • Manual IP updates via web interface
  • Browser-based update client for temporary/adhoc usage
  • Shows time since last update via API, whether it used TLS or not
  • Shows IP v4 and v6 addresses (from master nameserver records)
  • Shows client / server fault counters, available and abuse flags
  • Supports IP v4 and v6, TLS.
  • Easy and simple web interface, it tries to actively help to configure routers / update clients / nameservers.
  • Made with security and privacy in mind
  • No nagging, no spamming, no ads - trying not to annoy users
  • Free and open source software, made with Python and Django

Download Details:
Author: nsupdate-info
Source Code: https://github.com/nsupdate-info/nsupdate.info#features
License: View license

#python #django

Nsupdate: Dynamic DNS Service Written in Python
Grace  Edwards

Grace Edwards

1656348360

Top Github Repo for Django Developers

Github repo every DJANGO developer must know !! To improve Django Coding - This video is all about the best or top Django GitHub repository that a python or a Django developer should know. These repositories can help you to become a good python and Django developer.  These repositories contain all the best sources to improve your coding skills, modelling skills, and most important Django skills.

Over the past few years, there has been an increase in the number of programming languages and frameworks developed by programmers and organizations.Google developed Go. Facebook developed React (JS library). Microsoft developed TypeScript. However, in the last few years, the programming language that got extremely popular is none other than Python.
Well, the Python programming language is not new. In fact, it is actually older than Java. After its first release in 1991.

The increased usage of the Django framework for web development has contributed to Python’s growth, but it’s ultimately the ideal storm — just the right amount of developer and official support, as well as demand.

As a matter of fact, Python has been backed by the biggest tech company in the world, Google, since 2006. So much so that they have a developer page dedicated to Python that offers free training including exercises, lecture videos, and much more.

I have been using Python in my applications for a while and I have been fascinated by its simplicity yet powerful enough to do almost anything. While researching, I found these cool developer open-source Django projects on GitHub.

00:00 - About Best Django open Source Repository
01:41 - Repo 1  https://github.com/arrobalytics/django-ledger
03:06 - Repo 2 https://github.com/gothinkster/django-realworld-example-app
03:36 - Repo 3 https://github.com/wagtail/wagtail
05:48 - Repo 4 https://github.com/saleor/saleor 
07:24 - About Newton School
08:33 - Repo 5 https://github.com/openedx/edx-platform
09:43 - Open source Django Contribution

#python #django #github 

Top Github Repo for Django Developers
Zachary Palmer

Zachary Palmer

1656314490

How to Integrate GraphQL to Your Django Application

Adding a GraphQL endpoint to your Django application is a simple and powerful way to reduce API maintenance and give more power to your frontend developers.

There are a variety of choices when it comes to libraries to assist you in adding GraphQL support to your application. The two main contenders are Graphene and Strawberry, both of which having specific Django extensions (graphene-django and strawberry-graphql-django) that use introspection on your models in building out the types for the API.

Graphene vs. Strawberry

Graphene has been around for a while and works well. There does seem to be a maintenance issue as it has been awhile since a new release has landed though there is recent activity in the repo.

Strawberry on the other hand is a new comer with active development and releases and has commercial support. I find the API more pleasant in Strawberry as well though my experience with it is still too limited to have a strong opinion on which is best.

With both libraries you build up a set of Types that you want to access through Queries and manipulate through Mutations. Therefore, let's just do a quick side by side comparison of how you build up a GraphQL service for the following data model:

# A specific weightlifting movement, e.g. Bench Press, Squat, Clean
class Movement(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)


# Maximum reps of a weight of a Movement on a certain date
class Lift(models.Model):
    lifter = models.ForeignKey(User, on_delete=models.CASCADE)
    movement = models.ForeignKey(Movement, on_delete=models.CASCADE)
    reps = models.IntegerField()
    pounds = models.IntegerField()
    lifted_on = models.DateField()
    created_at = models.DateTimeField(default=timezone.now)

    def calculate_max(self, reps: int) -> int:
        ...

Types

With Graphene we'd have:

import graphene
from graphene_django import DjangoObjectType


class UserType(DjangoObjectType):
    class Meta:
        model = get_user_model()


class MovementType(DjangoObjectType):
    class Meta:
        model = Movement


class LiftType(DjangoObjectType):
    one_rep_max = graphene.Int()

    class Meta:
        model = Lift

    def resolve_one_rep_max(self, info) -> int:
        return self.calculate_max(1)

And with Strawberry there is:

import strawberry_django
from strawberry_django import


@strawberry_django.type(get_user_model())
class UserType:
    username: auto
    email: auto


@strawberry_django.type(Movement)
class MovementType:
    id: auto
    name: auto
    slug: auto


@strawberry_django.type(Lift)
class LiftType:
    id: auto
    movement: MovementType
    reps: auto
    pounds: auto
    lifted_on: auto
    created_at: auto

    @strawberry_django.field
    def one_rep_max(self) -> int:
        return self.calculate_max(1)

Queries and Mutations

With both Graphene and Strawberry you are building up a Schema object that consists of a Query and/or Mutation object that they themselves contain a set of methods that resolve into Types previously defined.

Building our our types, let's define some queries that allows us to query for Lifts as well as Movements and mutations that allow us to create, update and delete Lifts.

First in Graphene we'd have:

import typing

import graphene

from . import models
from .types import LiftType, MovementType


class Query(graphene.ObjectType):
    movements = graphene.List(MovementType)
    lifts = graphene.List(LiftType)

    def resolve_lifts(self, info, *args, **kwargs) -> list[LiftType]:
        return LiftType.get_queryset(
            models.Lift.objects.filter(lifter=info.context.user),
            info
        )


class LiftCreateMutation(graphene.Mutation):
    class Arguments:
        reps = graphene.Int(required=True)
        pounds = graphene.Int(required=True)
        lifted_on: = graphene.Date(required=True)
        movement_slug = graphene.String(required=True)

    ok = graphene.Boolean()
    lift = graphene.Field(LiftType)
    error = graphene.String()

    @classmethod
    def mutate(
        cls, root, info, reps: int, pounds: int, lifted_on: str, movement_slug: str, **kwargs
    ) -> "LiftCreateMutation":
        movement = models.Movement.objects.filter(slug=movement_slug).first()
        if movement is None:
            raise cls(ok=False, error="Movement doesn't exist.")

        lift = movement.lifts.create(
            lifter=info.context.user,
            reps=reps,
            pounds=pounds,
            lifted_on=lifted_on
        )
        return cls(ok=True, lift=lift)


class LiftUpdateMutation(graphene.Mutation):
    class Arguments:
        id = graphene.Int(required=True)
        reps = graphene.Int(required=True)
        pounds = graphene.Int(required=True)
        lifted_on: = graphene.Date(required=True)
        movement_slug = graphene.String(required=True)

    ok = graphene.Boolean()
    lift = graphene.Field(LiftType)
    error = graphene.String()

    @classmethod
    def mutate(
        cls, root, info, reps: int, pounds: int, lifted_on: str, movement_slug: str, **kwargs
    ) -> "LiftUpdateMutation":
        lift = models.Lift.objects.filter(
            lifter=info.context.user,
            id=id
        ).first()
        if lift is None:
            return cls(ok=False, error="Lift doesn't exist.")

        movement = models.Movement.objects.filter(slug=movement_slug).first()
        if movement is None:
            return cls(ok=False, error="Movement doesn't exist.")

        lift.movement = movement
        lift.reps = reps
        lift.pounds = pounds
        lift.lifted_on = lifted_on
        lift.save(update_fields=["movement", "reps", "pounds", "lifted_on"])

        return cls(ok=True, lift=lift)


class LiftDeleteMutation(graphene.Mutation):
    class Arguments:
        id = graphene.Int(required=True)

    ok = graphene.Boolean()
    error = graphene.String()

    @classmethod
    def mutate(
        cls, root, info, reps: int, pounds: int, lifted_on: str, movement_slug: str, **kwargs
    ) -> "LiftDeleteMutation":
        lift = models.Lift.objects.filter(
            lifter=info.context.user,
            id=id
        ).first()
        if lift is None:
            raise cls(ok=False, error="Lift doesn't exist.")

        lift.delete()

        return cls(ok=True)


class Mutation(graphene.ObjectType):
    lift_create = LiftCreateMutation.Field()
    lift_update = LiftUpdateMutation.Field()
    lift_delete = LiftDeleteMutation.Field()


schema = graphene.Schema(query=Query, mutation=Mutation)

And in Strawberry it would look like:

import typing
from datetime import date

import strawberry
import strawberry_django

from . import models
from .types import LiftType, MovementType


if typing.TYPE_CHECKING:  # pragma: no cover
    from typing import Any
    from django.http import HttpRequest, JsonResponse
    from strawberry.types import Info


class IsAuthenticated(strawberry.BasePermission):
    message = "User is not authenticated"

    def has_permission(self, source: "Any", info: "Info", **kwargs) -> bool:
        request: "HttpRequest" = info.context["request"]
        return request.user.is_authenticated


@strawberry.type
class Query:
    movements: list[MovementType] = strawberry_django.field()

    @strawberry.field(permission_classes=[IsAuthenticated])
    def lifts(self, info: "Info") -> list[LiftType]:
        return list(
            models.Lift.objects.filter(lifter=info.context.request.user)
        )


@strawberry.type
class Mutation:

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def lift_create(
        self,
        reps: int,
        pounds: int,
        lifted_on: date,
        movement_slug: str,
        info: "Info",
    ) -> LiftType:
        movement = models.Movement.objects.filter(slug=movement_slug).first()
        if movement is None:
            raise Exception("Movement doesn't exist.")

        return movement.lifts.create(
            lifter=info.context.request.user,
            reps=reps,
            pounds=pounds,
            lifted_on=lifted_on
        )

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def lift_update(
        self,
        id: int,
        reps: int,
        pounds: int,
        lifted_on: date,
        movement_slug: str,
        info: "Info",
    ) -> LiftType:
        lift = models.Lift.objects.filter(
            lifter=info.context.request.user,
            id=id
        ).first()
        if lift is None:
            raise Exception("Lift doesn't exist.")
        movement = models.Movement.objects.filter(slug=movement_slug).first()
        if movement is None:
            raise Exception("Movement doesn't exist.")

        lift.movement = movement
        lift.reps = reps
        lift.pounds = pounds
        lift.lifted_on = lifted_on
        lift.save(update_fields=["movement", "reps", "pounds", "lifted_on"])

        return lift

    @strawberry.mutation(permission_classes=[IsAuthenticated])
    def lift_delete(self, id: int, info: "Info") -> str:
        lift = models.Lift.objects.filter(
            lifter=info.context.request.user,
            id=id
        ).first()
        if lift is None:
            raise Exception("Lift doesn't exist.")

        lift.delete()

        return "ok"


schema = strawberry.Schema(query=Query, mutation=Mutation)

Exposing the Endpoint

Now that we have our schema instances we need to expose them the web. Both Graphene and Strawberry pretty much do the same thing here, passing the schema object into a prebuilt Django view that handles all the nitty-gritty of GraphQL requests and respones.

In Graphene:

from django.conf import settings
from django.urls import path
from django.views.decorators.csrf import csrf_exempt

from graphene_django.views import GraphQLView

from .schema import schema


urlpatterns = [
    path("graphql/", csrf_exempt(
        GraphQLView.as_view(graphiql=settings.DEBUG, schema=schema)
    )),
]

In Strawberry:

from django.conf import settings
from django.urls import path

from strawberry.django.views import GraphQLView

from .schema import schema


urlpatterns = [
    # Strawberry's GraphQLView pre-bakes the csrf_exempt decorator so you
    # do not need to add it like we do for Graphene
    path("graphql/", GraphQLView.as_view(
        graphiql=settings.DEBUG,
        schema=schema,
    ))
]

What do you think? Are you building anything with GraphQL in Django? I'd love to hear more about what you doing and if it's different than either of these approaches.

#graphql #api #django #python

How to Integrate GraphQL to Your Django Application
최 호민

최 호민

1656173400

Python 및 Django로 사전 애플리케이션을 빌드하는 방법

Python에서 Django 웹 프레임워크와 PyDictionary 라이브러리를 사용하여 간단한 사전 애플리케이션을 빌드하는 방법을 알아봅니다.

사전은 사용자가 특정 단어를 검색할 수 있도록 하고 해당 단어의 의미와 그 동의어 및 반의어를 대가로 제공하는 응용 프로그램입니다.

이 튜토리얼에서는 Python에서 Django 프레임워크와 PyDictionary API를 사용하여 영어 사전을 구축하는 방법을 배웁니다. 이 튜토리얼을 따라 하려면 앱의 프론트엔드에 사용될 HTML과 부트스트랩에 대한 기본적인 이해가 필요합니다.

Django 프레임워크와 PyDictionary API를 사용하기 전에 Django는 웹 애플리케이션을 빌드하는 데 사용되는 프레임워크이고 PyDictionary는 단어 의미, 동의어, 반의어 및 번역을 얻는 데 사용되는 API입니다.

PyDictionary API는 오프라인에서 작동하지 않습니다. API에 대한 성공적인 요청을 하려면 온라인 상태여야 합니다.

아래는 목차입니다.

  • 가상 환경 만들기
  • Django 및 PyDictionary 설치
  • 프로젝트 및 애플리케이션 생성
  • Settings.py 파일에 앱 등록
  • 앱의 URL 구성
  • 보기 만들기
  • HTML 템플릿 만들기
  • 검색어 기능 구현
  • 기능 테스트
  • 결론

가상 환경 만들기

우선 이 프로젝트를 위한 가상 환경을 만들고 이름을 지정하겠습니다 project. 이것은 규칙이 아닙니다. 원하는 이름을 지정할 수 있습니다. 아래 명령을 사용하세요.

$ python -m venv project

이제 다음 명령을 사용하여 가상 환경을 활성화합니다.

$ .\project\Scripts\activate

Django 및 PyDictionary 설치

그런 다음 아래와 같이 활성화된 가상 환경, Django 프레임워크 및 PyDictionary 내부에 필요한 라이브러리를 설치합니다.

$ pip install django PyDictionary

프로젝트 및 애플리케이션 생성

이제 Django가 성공적으로 설치되었으므로 Django 내장 명령을 사용하여 Django 프로젝트를 만들고  django-admin startproject터미널에서 다음 명령을 실행합니다.

$ django-admin startproject djangodictionary

위의 명령은 이라는 폴더를 생성  djangodictionary할 것입니다. 우리는 이 폴더 안에서 작업할 것입니다. 이제  폴더 cd 로 이동  djangodictionary 하여 Django 앱을 생성해 보겠습니다. 아래 명령을 실행합니다.

$ python manage.py startapp dictionary

Django를 성공적으로 설치하고 새 프로젝트를 생성한 후 설치가 성공했는지 확인하고 아래 명령을 실행합니다.

$ python manage.py runserver

manage.pyrunserver, startproject, startapp 등과 같은 터미널에서 Django 관리 명령을 실행하는 데 사용되는 스크립트 파일입니다. 스크립트 manage.py는 명령을 실행한 후 생성 django-admin startproject됩니다.

다음 출력이 나오는지 확인하십시오.

파이썬 관리.py 실행 서버

http://127.0.0.1:8000/ 을 브라우저에 복사  합니다. 아래 출력이 나오면 Django를 성공적으로 설치한 것입니다.

Django가 성공적으로 설치되었습니다.

Settings.py 파일에 앱 등록

Django에서 우리가 만드는 모든 앱은 사용하기 전에 등록해야 합니다. 이제 djangodictionary폴더 안에 라는 파일 settings.py이 있습니다. 이 파일은 전체 프로젝트의 설정을 구성하는 데 사용됩니다.

settings.py 파일에 앱 등록

파일을 열고 목록 settings.py까지 아래로 스크롤하여 INSTALLED_APPS목록을 다음과 같이 만듭니다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # external installed app
    # registering the dictionary app
    'dictionary',
]

앱의 URL 구성

이제 URL을 구성해 보겠습니다. Django에는 두 개의 urls.py파일이 있습니다. 첫 번째 파일은 Django와 함께 제공되며 모든 앱의 URL을 등록하는 데 사용되며 프로젝트 루트 폴더에 있고 두 번째 urls.py파일은 앱 내부에 생성됩니다. 폴더는 프로그래머에 의해 생성되며, 우리의 경우 dictionary폴더 안에 생성됩니다.

먼저 앱의 URL을 등록하고 urls.py프로젝트 루트 폴더에서 파일을 엽니다.

앱의 URL 구성

파일을 열고 다음 urls.py과 같은지 확인하십시오.

# importing the django's in-built admin url
from django.contrib import admin
# importing path and include from django's in-built urls
from django.urls import path, include

# defining the list for urls
urlpatterns = [
    path('admin/', admin.site.urls),
    # registering dictionary app urls in project
    path('', include('dictionary.urls')),
]

사전 앱의 URL을 등록했으므로 이제 dictionary폴더 안에 urls.py파일을 생성하겠습니다.

앱의 URL 구성

urls.py내에서 파일을 열고 다음을 추가합니다.dictionary

# from current folder, we are importing the two views, HomeView & SearchView
from . import views
# importing path from django's in-built urls
from django.urls import path

# defining the list for urls
urlpatterns = [
    path('', views.homeView, name='home'),#this is the home url
    path('search', views.searchView, name='search'),#this is the search url
]

보기 만들기

및는 아직 생성되지 않았으므로 이제 생성 homeViewsearchView보겠습니다. dictionary폴더 안에는 다음과 같은 views.py파일이 있습니다.

앱의 보기 만들기

이 파일을 열고 다음과 같이 만듭니다.

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render

# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')

# this is the view that will render search page
def searchView(request):
    return render(request, 'dictionary/search.html')

다음 섹션에서 폴더를 만들고 index.htmlsearch.html내부에 있을 것입니다.dictionary

HTML 템플릿 만들기

이제 Django에서 조금 벗어나 사전 앱의 기본 프론트엔드를 빌드해 보겠습니다. 분명히 우리는 앱의 콘텐츠에 HTML을 사용하고 콘텐츠의 스타일을 지정하는 데 부트스트랩을 사용할 것입니다.

dictionary폴더에 라는 폴더를 만들고 templates이 폴더 안에 templates라는 다른 폴더를 만듭니다 dictionary. 여기에서 Django가 모든 HTML 파일을 찾을 수 있습니다.

세 개의 HTML 파일, 즉 index.html, search.html, 및 base.html, 두 개의 파일 index.html을 만들고 search.html에서 상속합니다 base.html. 템플릿 상속은 Django와 함께 제공되는 기능 중 하나이며 반복하지 않을 것이기 때문에 편리합니다.

이제 다음 세 개의 HTML 파일을 생성해 보겠습니다.

HTML 파일 만들기

파일을 열고 base.html다음을 추가합니다.

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dictionary</title>
    <!-- CSS only -->
    <!-- we are getting bootstrap5 from the CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
 
<body>
    <div class="container mt-4">
        <div class="row">
 
            <div class="mt-4 p-5 bg-success text-white rounded mb-3">
                <h1>ThePythonCode.com Dictionary</h1>
            </div>
 
            <div class="col-md-12">
                {% block content %}
                <!-- here we will inject the content of every page that 
                    inherits from the base page -->
                {% endblock %}
            </div>
        </div>
    </div>
</body>
 
</html>

기본 HTML 및 부트스트랩 상용구. index.html파일 에서 상속 base.html하므로 다음을 추가 하십시오 index.html.

<!-- the index page is inheriting from the base page -->
<!-- the extends tags are used for inheriting from the base page -->
{% extends 'dictionary/base.html' %}

<!-- the block content tags for containing content of the page -->
{%  block content %}

<form action="search">
    <div class="input-group">
        <input type="text" required class="form-control" name="search" placeholder="Search your favorite word.......">
        <div class="input-group-append">
            <button class="btn btn-success" type="submit">
                Search
            </button>
        </div>
    </div>

</form>

{% endblock %}

여기까지는 아직 앱에 대한 느낌이 없었으므로 서버를 실행하여 테스트해 보겠습니다.

$ python manage.py runserver

서버를 시작한 후 브라우저로 이동하여 http://127.0.0.1:8000/ 페이지를 새로 고치면 아래 페이지를 얻을 수 있습니다.

PythonCode.com 사전 페이지

검색어 기능 구현

홈 인덱스 페이지가 성공적으로 작동했으므로 이제 Django로 돌아가서 이번에는 searchView.

views.py폴더 안의 파일을 열고 다음 dictionary을 편집합니다 searchView().

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render
# importing the PyDictionary library
from PyDictionary import PyDictionary


# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')


# this is the view that will render search page
def searchView(request):
    # capturing the word from the form via the name search
    word = request.GET.get('search')
    # creating a dictionary object
    dictionary = PyDictionary()
    # passing a word to the dictionary object
    meanings = dictionary.meaning(word)
    # getting a synonym and antonym  
    synonyms = dictionary.synonym(word)
    antonyms = dictionary.antonym(word)
    # bundling all the variables in the context  
    context = {
            'word': word,
            'meanings':meanings,
            'synonyms':synonyms,
            'antonoyms':antonyms
        }
    return render(request, 'dictionary/search.html', context)

우리는 PyDictionary주어진 단어의 의미, 동의어, 반의어를 얻기 위해 를 사용 context하고 있습니다. 그런 다음 에서 사용할 사전을 구성합니다 search.html.

열고 search.html아래에 추가:

<!-- the search page inherits from the base -->
{% extends 'dictionary/base.html' %}

{% block content %}
<!-- this will display the searched word -->
<h4>{{ word }}</h4>

<!-- this will display the word meaning -->
<p>{{ meanings }}</p>

<hr>
<!-- this will display the antonym for the word if its available-->
<p><b>Antonyms</b>:{{ antonym }}</p>
<hr>
<!-- this will display the synonym for the word if its available-->
<p><b>Synonyms</b>:{{ synonym }}</p>

{% endblock %}

기능 테스트

함수에서 검색 단어 기능을 구현했으므로 이제 searchView() 첫 단어 검색을 테스트해 보겠습니다. 브라우저에서 http://127.0.0.1:8000 을 복사하여 아래와 같이 출력합니다.

검색어 기능 테스트

서버가 실행 중인지 확인하고 그렇지 않은 경우 다음 명령을 다시 실행하십시오.

$ python manage.py runserver

이제 앱이 실행 중이므로 단어를 검색 "programming"하고 입력 필드에 단어를 입력하고 검색 버튼을 클릭합니다. 검색이 완료되면 아래와 같이 모든 결과가 표시되는 검색 페이지로 리디렉션됩니다.

검색어 기능 테스트

결론

이 튜토리얼은 여기까지입니다. 이제 Django 프레임워크와 PyDictionary API를 사용하는 방법을 알고 있기를 바랍니다.

이 튜토리얼에서는 Django와 PyDictionary를 사용하여 빌드할 수 있는 고급 항목을 고려하여 몇 가지 기본 사항을 다뤘습니다.

여기 에서 완전한 코드 를 얻으십시오 .

https://www.thepythoncode.com 의 원본 기사 출처

#python #django  

Python 및 Django로 사전 애플리케이션을 빌드하는 방법

Как создать приложение-словарь с помощью Python и Django

Узнайте, как создать простое приложение-словарь с использованием веб-фреймворка Django и библиотеки PyDictionary на Python.

Словарь — это приложение, которое позволяет пользователям искать определенное слово и взамен предоставляет значения слова, его синонима и антонима.

В этом руководстве вы узнаете, как создать словарь английского языка с помощью фреймворка Django и API PyDictionary в Python. Чтобы следовать этому руководству, необходимо иметь базовые знания HTML и начальной загрузки, которые будут использоваться для внешнего интерфейса приложения.

Прежде чем мы будем использовать платформу Django и API PyDictionary, давайте познакомимся с этими двумя: Django — это платформа, используемая для создания веб-приложений, а PyDictionary — это API, который используется для получения значений слов, синонимов, антонимов и переводов.

API PyDictionary не работает в автономном режиме, для выполнения успешных запросов к API необходимо подключение к сети.

Ниже приведено содержание:

  • Создание виртуальной среды
  • Установка Django и PyDictionary
  • Создание проекта и приложения
  • Регистрация приложения в файле Settings.py
  • Настройка URL-адресов приложения
  • Создание представлений
  • Создание шаблонов HTML
  • Реализация функциональности поискового слова
  • Тестирование функциональности
  • Вывод

Создание виртуальной среды

Давайте, прежде всего, создадим виртуальную среду для этого проекта, назовем ее project, это не соглашение, вы можете назвать ее как хотите, используйте команду ниже:

$ python -m venv project

Теперь активируйте виртуальную среду с помощью следующей команды:

$ .\project\Scripts\activate

Установка Django и PyDictionary

Затем мы установим необходимые библиотеки внутри активированной виртуальной среды, среды Django и PyDictionary, как показано ниже:

$ pip install django PyDictionary

Создание проекта и приложения

Теперь, когда Django успешно установлен, давайте создадим проект Django с помощью встроенной команды Django  django-admin startproject, запустив эту команду в своем терминале:

$ django-admin startproject djangodictionary

Приведенная выше команда создаст папку с именем  djangodictionary, мы будем работать внутри этой папки. Теперь  cd в  djangodictionary папку и давайте создадим приложение Django. Запустите следующую команду:

$ python manage.py startapp dictionary

После успешной установки Django и создания нового проекта, давайте посмотрим, прошла ли установка успешно, и выполните следующую команду:

$ python manage.py runserver

Это manage.pyфайл сценария, который используется для запуска административных команд Django в терминале, таких как runserver, startproject, startapp и т. д. manage.pyСценарий создается после запуска django-admin startprojectкоманды.

Убедитесь, что вы получили следующий вывод:

сервер запуска python manage.py

Скопируйте  http://127.0.0.1:8000/ в свой браузер, если вы получите следующий вывод, значит, вы успешно установили Django:

Джанго успешно установлен

Регистрация приложения в файле Settings.py

В Django каждое приложение, которое мы создаем, должно быть зарегистрировано перед его использованием, теперь внутри djangodictionaryпапки есть файл с именем settings.py, этот файл используется для настройки параметров всего проекта:

Регистрация приложения в файле settings.py

Откройте settings.pyфайл и прокрутите вниз до INSTALLED_APPSсписка, чтобы список выглядел следующим образом:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # external installed app
    # registering the dictionary app
    'dictionary',
]

Настройка URL-адресов приложения

Давайте теперь настроим наши URL-адреса в Django, у нас есть два urls.pyфайла, первый поставляется с Django и используется для регистрации всех URL-адресов приложений и находится в корневой папке проекта, а второй urls.pyфайл создается внутри приложения. папку программатором, в нашем случае она будет создана внутри dictionaryпапки.

Прежде всего, давайте зарегистрируем URL-адреса нашего приложения и откроем urls.pyфайл в корневой папке проекта:

Настройка URL-адресов приложения

Откройте urls.pyфайл и убедитесь, что он выглядит так, как показано ниже .

# importing the django's in-built admin url
from django.contrib import admin
# importing path and include from django's in-built urls
from django.urls import path, include

# defining the list for urls
urlpatterns = [
    path('admin/', admin.site.urls),
    # registering dictionary app urls in project
    path('', include('dictionary.urls')),
]

Теперь, когда мы зарегистрировали URL-адреса приложения-словаря, давайте создадим их внутри dictionaryпапки и создадим urls.pyфайл:

Настройка URL-адресов приложения

Откройте urls.pyфайл внутри dictionaryприложения и добавьте следующее:

# from current folder, we are importing the two views, HomeView & SearchView
from . import views
# importing path from django's in-built urls
from django.urls import path

# defining the list for urls
urlpatterns = [
    path('', views.homeView, name='home'),#this is the home url
    path('search', views.searchView, name='search'),#this is the search url
]

Создание представлений

И homeViewеще searchViewне созданы, давайте теперь их создадим. Внутри dictionaryпапки находится views.pyфайл:

Создание представлений приложения

Откройте этот файл и сделайте так, чтобы он выглядел так:

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render

# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')

# this is the view that will render search page
def searchView(request):
    return render(request, 'dictionary/search.html')

Мы будем создавать index.htmlи search.htmlвнутри dictionaryпапки в следующем разделе.

Создание шаблонов HTML

Теперь давайте немного отойдем от Django и создадим базовый интерфейс приложения-словаря. Очевидно, что мы будем использовать HTML для содержимого приложения и начальную загрузку для стилизации содержимого.

В dictionaryпапке создайте папку с именем templates, а внутри этой templatesпапки создайте еще одну папку с именем dictionary, здесь Django найдет все файлы HTML.

Мы создадим три HTML-файла, а именно index.html, search.htmlи base.html, два файла index.htmlи search.htmlунаследуем их от файла base.html. Наследование шаблонов — это одна из функций Django, и она очень удобна, потому что мы не будем повторяться.

Теперь давайте создадим эти три файла HTML:

Создание файлов HTML

Откройте base.htmlфайл и добавьте следующее:

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dictionary</title>
    <!-- CSS only -->
    <!-- we are getting bootstrap5 from the CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
 
<body>
    <div class="container mt-4">
        <div class="row">
 
            <div class="mt-4 p-5 bg-success text-white rounded mb-3">
                <h1>ThePythonCode.com Dictionary</h1>
            </div>
 
            <div class="col-md-12">
                {% block content %}
                <!-- here we will inject the content of every page that 
                    inherits from the base page -->
                {% endblock %}
            </div>
        </div>
    </div>
</body>
 
</html>

Базовый шаблон HTML и Bootstrap. Будет index.htmlнаследоваться от base.htmlфайла, поэтому добавьте следующее index.html:

<!-- the index page is inheriting from the base page -->
<!-- the extends tags are used for inheriting from the base page -->
{% extends 'dictionary/base.html' %}

<!-- the block content tags for containing content of the page -->
{%  block content %}

<form action="search">
    <div class="input-group">
        <input type="text" required class="form-control" name="search" placeholder="Search your favorite word.......">
        <div class="input-group-append">
            <button class="btn btn-success" type="submit">
                Search
            </button>
        </div>
    </div>

</form>

{% endblock %}

Зайдя так далеко, мы еще не почувствовали наше приложение, поэтому давайте проверим его, запустив сервер:

$ python manage.py runserver

После запуска сервера перейдите в браузер и обновите страницу http://127.0.0.1:8000/ , вы сможете получить следующую страницу:

Страница словаря ThePythonCode.com

Реализация функциональности поискового слова

Теперь, когда домашняя индексная страница успешно работает, давайте теперь вернемся к Django, на этот раз мы хотим реализовать функцию поиска слов через файл searchView.

Откройте views.pyфайл внутри dictionaryпапки и отредактируйте searchView():

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render
# importing the PyDictionary library
from PyDictionary import PyDictionary


# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')


# this is the view that will render search page
def searchView(request):
    # capturing the word from the form via the name search
    word = request.GET.get('search')
    # creating a dictionary object
    dictionary = PyDictionary()
    # passing a word to the dictionary object
    meanings = dictionary.meaning(word)
    # getting a synonym and antonym  
    synonyms = dictionary.synonym(word)
    antonyms = dictionary.antonym(word)
    # bundling all the variables in the context  
    context = {
            'word': word,
            'meanings':meanings,
            'synonyms':synonyms,
            'antonoyms':antonyms
        }
    return render(request, 'dictionary/search.html', context)

Мы используем PyDictionary, чтобы получить значение, синоним и антоним данного слова, затем мы создаем contextсловарь, который будем использовать в файле search.html.

Откройте search.htmlи добавьте ниже:

<!-- the search page inherits from the base -->
{% extends 'dictionary/base.html' %}

{% block content %}
<!-- this will display the searched word -->
<h4>{{ word }}</h4>

<!-- this will display the word meaning -->
<p>{{ meanings }}</p>

<hr>
<!-- this will display the antonym for the word if its available-->
<p><b>Antonyms</b>:{{ antonym }}</p>
<hr>
<!-- this will display the synonym for the word if its available-->
<p><b>Synonyms</b>:{{ synonym }}</p>

{% endblock %}

Тестирование функциональности

Теперь, когда нам удалось реализовать функцию поиска слова в searchView() функции, давайте протестируем наш поиск по первому слову. Скопируйте http://127.0.0.1:8000 в браузере, чтобы получить результат ниже:

Тестирование функциональности поискового слова

Убедитесь, что сервер запущен, если нет, то повторите эту команду:

$ python manage.py runserver

Теперь, когда приложение запущено, мы будем искать слово "programming", вводить слово в поле ввода и нажимать кнопку поиска. После завершения поиска вы будете перенаправлены на страницу поиска, где отображаются все результаты, как показано ниже:

Тестирование функциональности поискового слова

Вывод

Вот и все для этого руководства, теперь мы надеемся, что вы знаете, как играть с инфраструктурой Django и API PyDictionary.

Обратите внимание, что в этом уроке мы только что рассмотрели несколько основных вещей, учитывая более сложные вещи, которые вы можете создать, используя эти два, Django и PyDictionary.

Получите полный код здесь .

Оригинальный источник статьи на https://www.thepythoncode.com

#python #django 

Как создать приложение-словарь с помощью Python и Django

Como construir um aplicativo de dicionário com Python e Django

Aprenda a construir um aplicativo de dicionário simples usando a estrutura da Web do Django e a biblioteca PyDictionary em Python.

Um dicionário é um aplicativo que permite aos usuários pesquisar uma palavra específica e fornecer os significados da palavra e seu sinônimo e antônimo em troca.

Neste tutorial, você aprenderá como construir um dicionário de inglês com a ajuda do framework Django e da API PyDictionary em Python. Para acompanhar este tutorial, é necessário ter um conhecimento básico de HTML e bootstrap que serão usados ​​para o frontend do aplicativo.

Antes de usarmos o framework Django e a API PyDictionary, vamos conhecer esses dois, o Django é um framework usado para construir aplicações web e o PyDictionary é uma API que é usada para obter significados de palavras, sinônimos, antônimos e traduções.

A API PyDictionary não funciona offline, é preciso estar online para fazer solicitações bem-sucedidas à API.

Segue abaixo o índice:

  • Criando o ambiente virtual
  • Instalando o Django e o PyDictionary
  • Criando o projeto e o aplicativo
  • Registrando o aplicativo no arquivo Settings.py
  • Configurando as URLs do aplicativo
  • Criando visualizações
  • Fazendo os modelos HTML
  • Implementando a funcionalidade de pesquisa de palavras
  • Testando a funcionalidade
  • Conclusão

Criando o ambiente virtual

Vamos, antes de tudo, criar o ambiente virtual para este projeto, vamos nomeá-lo project, isso não é a convenção, você pode nomeá-lo como quiser, use o comando abaixo:

$ python -m venv project

Agora ative o ambiente virtual usando o seguinte comando:

$ .\project\Scripts\activate

Instalando o Django e o PyDictionary

Em seguida, instalaremos as bibliotecas necessárias dentro do ambiente virtual ativado, o framework Django e o PyDictionary conforme mostrado abaixo:

$ pip install django PyDictionary

Criando o projeto e o aplicativo

Agora que o Django foi instalado com sucesso, vamos criar um projeto Django usando o comando interno do Django  django-admin startproject, execute este comando em seu terminal:

$ django-admin startproject djangodictionary

O comando acima irá criar uma pasta chamada  djangodictionary, estaremos trabalhando dentro desta pasta. Agora  cd na  djangodictionary pasta e vamos criar um aplicativo Django. Execute o comando abaixo:

$ python manage.py startapp dictionary

Após instalar o Django com sucesso e criar o novo projeto, vamos ver se a instalação foi bem sucedida, execute o comando abaixo:

$ python manage.py runserver

O manage.pyé um arquivo de script que é usado para executar comandos administrativos do Django no terminal como o runserver, startproject, startapp, etc. O manage.pyscript é criado após a execução do django-admin startprojectcomando.

Certifique-se de obter a seguinte saída:

python manage.py runserver

Copie  http://127.0.0.1:8000/ em seu navegador, se você obtiver a saída abaixo, você instalou o Django com sucesso:

Django instalado com sucesso

Registrando o aplicativo no arquivo Settings.py

No Django, todo aplicativo que criamos tem que ser registrado antes de usarmos, agora dentro da djangodictionarypasta, existe um arquivo chamado settings.py, este arquivo é usado para definir as configurações de todo o projeto:

Registrando o aplicativo no arquivo settings.py

Abra o settings.pyarquivo e role para baixo até a INSTALLED_APPSlista, faça a lista agora ter a seguinte aparência:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # external installed app
    # registering the dictionary app
    'dictionary',
]

Configurando as URLs do aplicativo

Vamos agora configurar nossas URLs, no Django temos dois urls.pyarquivos, o primeiro vem com o Django e é usado para registrar as URLs de todos os aplicativos e fica na pasta raiz do projeto, enquanto o segundo urls.pyarquivo é criado dentro da pasta do aplicativo pasta pelo programador, no nosso caso ela será criada dentro da dictionarypasta.

Antes de mais nada, vamos registrar as URLs do nosso aplicativo e abrir o urls.pyarquivo na pasta raiz do projeto:

Configurando os URLs do aplicativo

Abra o urls.pyarquivo e verifique se ele se parece com abaixo

# importing the django's in-built admin url
from django.contrib import admin
# importing path and include from django's in-built urls
from django.urls import path, include

# defining the list for urls
urlpatterns = [
    path('admin/', admin.site.urls),
    # registering dictionary app urls in project
    path('', include('dictionary.urls')),
]

Agora que registramos as URLs do aplicativo de dicionário, vamos criá-las, dentro da dictionarypasta, criar um urls.pyarquivo:

Configurando os URLs do aplicativo

Abra o urls.pyarquivo dentro do dictionaryaplicativo e adicione o seguinte:

# from current folder, we are importing the two views, HomeView & SearchView
from . import views
# importing path from django's in-built urls
from django.urls import path

# defining the list for urls
urlpatterns = [
    path('', views.homeView, name='home'),#this is the home url
    path('search', views.searchView, name='search'),#this is the search url
]

Criando visualizações

Os homeViewe searchViewainda não foram criados, vamos agora criá-los. Dentro da dictionarypasta, há um views.pyarquivo:

Criando as visualizações do aplicativo

Abra este arquivo e faça com que fique assim:

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render

# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')

# this is the view that will render search page
def searchView(request):
    return render(request, 'dictionary/search.html')

Estaremos criando index.htmle search.htmldentro da dictionarypasta na próxima seção.

Fazendo os modelos HTML

Agora vamos nos afastar um pouco do Django e construir o frontend básico do aplicativo de dicionário. Obviamente, usaremos HTML para o conteúdo do aplicativo e bootstrap para estilizar o conteúdo.

Na dictionarypasta, crie uma pasta chamada templates, e dentro desta templatespasta crie outra pasta chamada dictionary, é onde o Django encontrará todos os arquivos HTML.

Criaremos três arquivos HTML, a saber index.html, search.html, e base.html, os dois arquivos index.htmle search.htmlherdaremos do arquivo base.html. A herança de template é um dos recursos que vem com o Django, e é útil porque não vamos nos repetir.

Agora vamos criar esses três arquivos HTML:

Criando os arquivos HTML

Abra o base.htmlarquivo e adicione o seguinte:

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dictionary</title>
    <!-- CSS only -->
    <!-- we are getting bootstrap5 from the CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
 
<body>
    <div class="container mt-4">
        <div class="row">
 
            <div class="mt-4 p-5 bg-success text-white rounded mb-3">
                <h1>ThePythonCode.com Dictionary</h1>
            </div>
 
            <div class="col-md-12">
                {% block content %}
                <!-- here we will inject the content of every page that 
                    inherits from the base page -->
                {% endblock %}
            </div>
        </div>
    </div>
</body>
 
</html>

HTML básico e clichê de Bootstrap. O index.htmlherdará do base.htmlarquivo, então adicione o seguinte a index.html:

<!-- the index page is inheriting from the base page -->
<!-- the extends tags are used for inheriting from the base page -->
{% extends 'dictionary/base.html' %}

<!-- the block content tags for containing content of the page -->
{%  block content %}

<form action="search">
    <div class="input-group">
        <input type="text" required class="form-control" name="search" placeholder="Search your favorite word.......">
        <div class="input-group-append">
            <button class="btn btn-success" type="submit">
                Search
            </button>
        </div>
    </div>

</form>

{% endblock %}

Chegando até aqui, ainda não conhecemos nosso aplicativo, então vamos testá-lo executando o servidor:

$ python manage.py runserver

Depois de iniciar o servidor, vá para o navegador e atualize a página http://127.0.0.1:8000/ , você deve conseguir a página abaixo:

Página do dicionário ThePythonCode.com

Implementando a funcionalidade de pesquisa de palavras

Agora que a página inicial do índice está funcionando com sucesso, vamos agora voltar ao Django, desta vez queremos implementar a funcionalidade de pesquisa de palavras através do searchView.

Abra o views.pyarquivo dentro da dictionarypasta e edite searchView():

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render
# importing the PyDictionary library
from PyDictionary import PyDictionary


# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')


# this is the view that will render search page
def searchView(request):
    # capturing the word from the form via the name search
    word = request.GET.get('search')
    # creating a dictionary object
    dictionary = PyDictionary()
    # passing a word to the dictionary object
    meanings = dictionary.meaning(word)
    # getting a synonym and antonym  
    synonyms = dictionary.synonym(word)
    antonyms = dictionary.antonym(word)
    # bundling all the variables in the context  
    context = {
            'word': word,
            'meanings':meanings,
            'synonyms':synonyms,
            'antonoyms':antonyms
        }
    return render(request, 'dictionary/search.html', context)

Estamos usando PyDictionarypara obter o significado, sinônimo e antônimo da palavra dada, então construímos o contextdicionário que usaremos no arquivo search.html.

Abra o search.htmle adicione abaixo:

<!-- the search page inherits from the base -->
{% extends 'dictionary/base.html' %}

{% block content %}
<!-- this will display the searched word -->
<h4>{{ word }}</h4>

<!-- this will display the word meaning -->
<p>{{ meanings }}</p>

<hr>
<!-- this will display the antonym for the word if its available-->
<p><b>Antonyms</b>:{{ antonym }}</p>
<hr>
<!-- this will display the synonym for the word if its available-->
<p><b>Synonyms</b>:{{ synonym }}</p>

{% endblock %}

Testando a funcionalidade

Agora que conseguimos implementar a funcionalidade de pesquisa de palavras na searchView() função, vamos testar nossa pesquisa de primeira palavra. Copie o http://127.0.0.1:8000 no navegador para obter a saída abaixo:

Testando a funcionalidade da palavra de pesquisa

Certifique-se de que o servidor esteja em execução, caso contrário, execute novamente este comando:

$ python manage.py runserver

Agora que o aplicativo está em execução, procuraremos a palavra "programming", digite a palavra no campo de entrada e clique no botão de pesquisa. Após a conclusão da pesquisa, você será redirecionado para a página de pesquisa onde são exibidos todos os resultados, conforme abaixo:

Testando a funcionalidade da palavra de pesquisa

Conclusão

É isso para este tutorial, agora esperamos que você saiba como brincar com o framework Django e a API PyDictionary.

Observe que neste tutorial acabamos de abordar algumas coisas básicas, considerando as coisas mais avançadas que você pode construir usando esses dois, Django e PyDictionary.

Obtenha o código completo aqui .

Fonte do artigo original em https://www.thepythoncode.com

#python #django 

Como construir um aplicativo de dicionário com Python e Django
Poppy Cooke

Poppy Cooke

1656155520

Comment créer une application de dictionnaire avec Python et Django

Apprenez à créer une application de dictionnaire simple à l'aide du framework Web Django et de la bibliothèque PyDictionary en Python.

Un dictionnaire est une application qui permet aux utilisateurs de rechercher un mot spécifique et fournit les significations du mot et son synonyme et antonyme en retour.

Dans ce didacticiel, vous apprendrez à créer un dictionnaire anglais à l'aide du framework Django et de l'API PyDictionary en Python. Pour suivre ce didacticiel, il faut avoir une compréhension de base du HTML et du bootstrap qui seront utilisés pour l'interface de l'application.

Avant d'utiliser le framework Django et l'API PyDictionary, apprenons à connaître ces deux, Django est un framework utilisé pour créer des applications Web et PyDictionary est une API utilisée pour obtenir la signification des mots, des synonymes, des antonymes et des traductions.

L'API PyDictionary ne fonctionne pas hors ligne, il faut être en ligne pour faire des requêtes réussies à l'API.

Ci-dessous la table des matières :

  • Création de l'environnement virtuel
  • Installer Django et PyDictionary
  • Création du projet et de l'application
  • Enregistrement de l'application dans le fichier Settings.py
  • Configuration des URL de l'application
  • Création de vues
  • Faire les modèles HTML
  • Implémentation de la fonctionnalité de recherche de mot
  • Test de la fonctionnalité
  • Conclusion

Création de l'environnement virtuel

Créons tout d'abord l'environnement virtuel de ce projet, nommons le project, ce n'est pas la convention, vous pouvez le nommer comme vous voulez, utilisez la commande ci-dessous :

$ python -m venv project

Activez maintenant l'environnement virtuel à l'aide de la commande suivante :

$ .\project\Scripts\activate

Installer Django et PyDictionary

Nous installerons ensuite les bibliothèques requises dans l'environnement virtuel activé, le framework Django et PyDictionary comme indiqué ci-dessous :

$ pip install django PyDictionary

Création du projet et de l'application

Maintenant que Django a été installé avec succès, créons un projet Django à l'aide de la commande intégrée Django  django-admin startproject, exécutez cette commande dans votre terminal :

$ django-admin startproject djangodictionary

La commande ci-dessus créera un dossier appelé  djangodictionary, nous travaillerons dans ce dossier. Maintenant  cd dans le  djangodictionary dossier et créons une application Django. Exécutez la commande ci-dessous :

$ python manage.py startapp dictionary

Après avoir installé Django avec succès et créé le nouveau projet, voyons si l'installation a réussi, exécutez la commande ci-dessous :

$ python manage.py runserver

Il manage.pys'agit d'un fichier de script utilisé pour exécuter les commandes d'administration de Django dans le terminal telles que runserver, startproject, startapp, etc. Le manage.pyscript est créé après l'exécution de la django-admin startprojectcommande.

Assurez-vous d'obtenir le résultat suivant :

serveur d'exécution python manage.py

Copiez  http://127.0.0.1:8000/ dans votre navigateur, si vous obtenez le résultat ci-dessous, vous avez installé Django avec succès :

Django installé avec succès

Enregistrement de l'application dans le fichier Settings.py

Dans Django, chaque application que nous créons doit être enregistrée avant de l'utiliser, maintenant à l'intérieur du djangodictionarydossier, il y a un fichier appelé settings.py, ce fichier est utilisé pour configurer les paramètres de l'ensemble du projet :

Enregistrement de l'application dans le fichier settings.py

Ouvrez le settings.pyfichier et faites défiler jusqu'à la INSTALLED_APPSliste, faites en sorte que la liste ressemble maintenant à :

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # external installed app
    # registering the dictionary app
    'dictionary',
]

Configuration des URL de l'application

Configurons maintenant nos URL, dans Django, nous avons deux urls.pyfichiers, le premier est fourni avec Django et est utilisé pour enregistrer toutes les URL des applications et il se trouve dans le dossier racine du projet, tandis que le second urls.pyfichier est créé à l'intérieur de l'application. dossier par le programmeur, dans notre cas, il sera créé à l'intérieur du dictionarydossier.

Tout d'abord, enregistrons les URL de notre application et ouvrons le urls.pyfichier dans le dossier racine du projet :

Configuration des URL de l'application

Ouvrez le urls.pyfichier et assurez-vous qu'il ressemble à ci-dessous

# importing the django's in-built admin url
from django.contrib import admin
# importing path and include from django's in-built urls
from django.urls import path, include

# defining the list for urls
urlpatterns = [
    path('admin/', admin.site.urls),
    # registering dictionary app urls in project
    path('', include('dictionary.urls')),
]

Maintenant que nous avons enregistré les URL de l'application de dictionnaire, créons-les maintenant, dans le dictionarydossier, créons un urls.pyfichier :

Configuration des URL de l'application

Ouvrez le urls.pyfichier dans l' dictionaryapplication et ajoutez ce qui suit :

# from current folder, we are importing the two views, HomeView & SearchView
from . import views
# importing path from django's in-built urls
from django.urls import path

# defining the list for urls
urlpatterns = [
    path('', views.homeView, name='home'),#this is the home url
    path('search', views.searchView, name='search'),#this is the search url
]

Création de vues

Les homeViewet searchViewn'ont pas encore été créés, créons-les maintenant. Dans le dictionarydossier, il y a un views.pyfichier :

Création des vues de l'application

Ouvrez ce fichier et faites-le ressembler à ceci :

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render

# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')

# this is the view that will render search page
def searchView(request):
    return render(request, 'dictionary/search.html')

Nous allons créer index.htmlet search.htmlà l'intérieur du dictionarydossier dans la prochaine section.

Faire les modèles HTML

Maintenant, éloignons-nous un peu de Django et construisons l'interface de base de l'application de dictionnaire. Évidemment, nous utiliserons HTML pour le contenu de l'application et bootstrap pour styliser le contenu.

Dans le dictionarydossier, créez un dossier appelé templates, et à l'intérieur de ce templatesdossier créez un autre dossier appelé dictionary, c'est là que Django trouvera tous les fichiers HTML.

Nous allons créer trois fichiers HTML, à savoir index.html, search.html, et base.html, les deux fichiers index.htmlet search.htmlhériterons du base.html. L'héritage de modèles est l'une des fonctionnalités fournies avec Django, et il est pratique car nous ne nous répéterons pas.

Créons maintenant ces trois fichiers HTML :

Création des fichiers HTML

Ouvrez le base.htmlfichier et ajoutez ce qui suit :

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dictionary</title>
    <!-- CSS only -->
    <!-- we are getting bootstrap5 from the CDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
 
<body>
    <div class="container mt-4">
        <div class="row">
 
            <div class="mt-4 p-5 bg-success text-white rounded mb-3">
                <h1>ThePythonCode.com Dictionary</h1>
            </div>
 
            <div class="col-md-12">
                {% block content %}
                <!-- here we will inject the content of every page that 
                    inherits from the base page -->
                {% endblock %}
            </div>
        </div>
    </div>
</body>
 
</html>

Code HTML de base et Bootstrap passe-partout. Le index.htmlhéritera du base.htmlfichier, ajoutez donc ce qui suit àindex.html :

<!-- the index page is inheriting from the base page -->
<!-- the extends tags are used for inheriting from the base page -->
{% extends 'dictionary/base.html' %}

<!-- the block content tags for containing content of the page -->
{%  block content %}

<form action="search">
    <div class="input-group">
        <input type="text" required class="form-control" name="search" placeholder="Search your favorite word.......">
        <div class="input-group-append">
            <button class="btn btn-success" type="submit">
                Search
            </button>
        </div>
    </div>

</form>

{% endblock %}

Arrivés aussi loin, nous n'avons pas encore eu une idée de notre application, alors testons-la en exécutant le serveur :

$ python manage.py runserver

Après avoir démarré le serveur, accédez au navigateur et actualisez la page http://127.0.0.1:8000/ , vous devriez pouvoir obtenir la page ci-dessous :

La page Dictionnaire de ThePythonCode.com

Implémentation de la fonctionnalité de recherche de mot

Maintenant que la page d'index d'accueil fonctionne correctement, revenons maintenant à Django, cette fois nous voulons implémenter la fonctionnalité de mot de recherche via le searchView.

Ouvrez le views.pyfichier dans le dictionarydossier et modifiezsearchView() :

# importing the render function from django.shortcuts
# the render function renders templates
from django.shortcuts import render
# importing the PyDictionary library
from PyDictionary import PyDictionary


# this is the view that will render the index page
def homeView(request):
    return render(request, 'dictionary/index.html')


# this is the view that will render search page
def searchView(request):
    # capturing the word from the form via the name search
    word = request.GET.get('search')
    # creating a dictionary object
    dictionary = PyDictionary()
    # passing a word to the dictionary object
    meanings = dictionary.meaning(word)
    # getting a synonym and antonym  
    synonyms = dictionary.synonym(word)
    antonyms = dictionary.antonym(word)
    # bundling all the variables in the context  
    context = {
            'word': word,
            'meanings':meanings,
            'synonyms':synonyms,
            'antonoyms':antonyms
        }
    return render(request, 'dictionary/search.html', context)

Nous utilisons PyDictionarypour obtenir le sens, le synonyme et l'antonyme du mot donné, nous construisons ensuite le contextdictionnaire que nous utiliserons dans le search.html.

Ouvrez le search.htmlet ajoutez ci-dessous :

<!-- the search page inherits from the base -->
{% extends 'dictionary/base.html' %}

{% block content %}
<!-- this will display the searched word -->
<h4>{{ word }}</h4>

<!-- this will display the word meaning -->
<p>{{ meanings }}</p>

<hr>
<!-- this will display the antonym for the word if its available-->
<p><b>Antonyms</b>:{{ antonym }}</p>
<hr>
<!-- this will display the synonym for the word if its available-->
<p><b>Synonyms</b>:{{ synonym }}</p>

{% endblock %}

Test de la fonctionnalité

Maintenant que nous avons réussi à implémenter la fonctionnalité de mot de recherche dans la searchView() fonction, testons notre recherche du premier mot. Copiez le http://127.0.0.1:8000 dans le navigateur pour obtenir le résultat ci-dessous :

Test de la fonctionnalité du mot de recherche

Assurez-vous que le serveur est en cours d'exécution, sinon relancez cette commande :

$ python manage.py runserver

Maintenant que l'application est en cours d'exécution, nous allons rechercher le mot "programming", entrer le mot dans le champ de saisie et cliquer sur le bouton de recherche. Une fois la recherche terminée, vous serez redirigé vers la page de recherche où tous les résultats sont affichés, comme ci-dessous :

Test de la fonctionnalité du mot de recherche

Conclusion

C'est tout pour ce tutoriel, nous espérons maintenant que vous savez jouer avec le framework Django et l'API PyDictionary.

Notez que dans ce didacticiel, nous venons de couvrir quelques éléments de base en tenant compte des éléments plus avancés que vous pourriez créer à l'aide de ces deux éléments, Django et PyDictionary.

Obtenez le code complet ici .

Source de l'article original sur https://www.thepythoncode.com

#python #django 

Comment créer une application de dictionnaire avec Python et Django