반응형
동적 이미지
라이브러리
# pip install requests
pip install pillow
view
# hottrack/views.py
# View 함수에서 동적으로 이미지 생성 및 응답
# - 동적으로 이미지/엑셀파일/PDF 등을 생성 시애 디스크에 파일을 저장하지 않고, 응답 가능 # (보다 빠른 응답 가능, 디스크 사용이 불가능한 서버에서도 유용)
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from hottrack.utils.cover import make_cover_image
def cover_png(request, pk):
# 최대값 512, 기본값 256
canvas_size = min(512, int(request.GET.get("size", 256)))
song = get_object_or_404(Song, pk=pk)
cover_image = make_cover_image(
song.cover_url, song.artist_name, canvas_size=canvas_size
)
# param fp : filename (str), pathlib.Path object or file object
# image.save("image.png")
response = HttpResponse(content_type="image/png")
cover_image.save(response, format="png")
return response
url
# hottrack/urls.py
urlpatterns += [
path(route="<int:pk>/cover.png", view=views.cover_png),
]
util
# melon/utils/cover.py
from io import BytesIO
import requests
from PIL import Image, ImageDraw, ImageFont, __version__ as pil_version # PIL 버전 10부터 변경되는 동작이 있어서, 버전을 체크해줍니다.
PIL_VERSION = tuple(map(int, pil_version.split(".")))
def make_cover_image(cover_url: str, text: str, canvas_size: int = 256) -> Image:
canvas = Image.new("RGB", (canvas_size, canvas_size), "white")
draw = ImageDraw.Draw(canvas)
res = requests.get(cover_url)
if res.ok:
cover_image = Image.open(BytesIO(res.content))
cover_image.thumbnail((canvas_size, canvas_size))
canvas.paste(cover_image, (0, 0))
else:
# 이미지 다운로드에 실패했을 경우, X 표시를 그립니다. draw.line((0, 0, canvas_size, canvas_size), fill="red") draw.line((0, canvas_size, canvas_size, 0), fill="blue")
# 사선 스트라이프 패턴 그리기 (회색, 선 굵기 3)
for i in range(-canvas_size, canvas_size, 10):
draw.line([(i, 0), (i + canvas_size, canvas_size)], fill="#F0F0F0", width=2)
# 맑은고딕 (윈도우), 애플고딕(맥) 폰트 사용하기 trying_font_names = ["malgunsl.ttf", "AppleGothic.ttf"]
for font_name in trying_font_names:
try:
font = ImageFont.truetype(font=font_name, size=32)
break
except IOError:
continue
else:
font = ImageFont.load_default()
# 텍스트를 그릴 시작 좌표 x, y 좌표 계산하기 if PIL_VERSION >= (10,):
x0 = int(canvas.width / 2)
y0 = int(canvas.height / 2)
bb_l, bb_t, bb_r, bb_b = draw.textbbox(xy=(0, 0), text=text, font=font)
x = x0 - (bb_r - bb_l) / 2
y = y0 - (bb_b - bb_t) / 2
else:
text_width, text_height = draw.textsize(text=text, font=font)
x = (canvas.width - text_width) / 2
y = (canvas.height - text_height) / 2
# 지정 좌표, 폰트, 색상으로 텍스트 그리기
draw.text(xy=(x, y), text=text, fill="black", font=font)
return canvas
CSV
라이브러리
pip install pandas
view
# hottrack/views.py
from io import BytesIO
import pandas as pd
def export_csv(request: HttpRequest) -> HttpResponse:
song_qs: QuerySet = Song.objects.all()
# .values() : 지정한 필드로 구성된 사전 리스트를 반환
song_qs = song_qs.values()
# 원하는 필드만 지정해서 뽑을 수도 있습니다.
# song_qs = song_qs.values("rank", "name", "artist_name", "like_count")
# 사전 리스트를 인자로 받아서, DataFrame을 생성할 수 있습니다. df = pd.DataFrame(data=song_qs)
# 메모리 파일 객체에 CSV 데이터를 저장합니다.
# CSV를 HttpResponse에 바로 저장할 때 utf-8-sig 인코딩이 적용되지 않아서 # BytesIO를 사용해서 인코딩을 적용한 후, HttpResponse에 저장합니다. export_file = BytesIO()
# df.to_csv("hottrack.csv", index=False) # 지정 파일로 저장할 수도 있고, 파일 객체를 전달할 수도 있습니다. # (한글깨짐방지) 한글 엑셀에서는 CSV 텍스트 파일을 해석하는 기본 인코딩이 cp949이기에
# utf-8-sig 인코딩을 적용하여 생성되는 CSV 파일에 UTF-8 BOM이 추가합니다.
df.to_csv(path_or_buf=export_file, index=False, encoding="utf-8-sig") # noqa
# 저장된 파일의 전체 내용을 HttpResponse에 전달합니다.
response = HttpResponse(content=export_file.getvalue(), content_type="text/csv") response["Content-Disposition"] = 'attachment; filename="hottrack.csv"'
return response
# hottrack/urls.py
urlpatterns = [
# ...
path(route="export.csv", view=views.export_csv),
]
엑셀
라이브러리
pip install pandas
pip install openpyxl
view
# hottrack/views.py
from io import BytesIO
import pandas as pd
from django.http import HttpResponseBadRequest
def export(request: HttpRequest, format: Literal["csv", "xlsx"]) -> HttpResponse:
song_qs: QuerySet = Song.objects.all()
song_qs = song_qs.values()
df = pd.DataFrame(data=song_qs)
export_file = BytesIO()
if format == "csv":
content_type = "text/csv"
filename = "hottrack.csv"
df.to_csv(path_or_buf=export_file, index=False, encoding="utf-8-sig") # noqa
elif format == "xlsx":
content_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
filename = "hottrack.xlsx"
df.to_excel(excel_writer=export_file, index=False, engine="openpyxl") # noqa
else:
return HttpResponseBadRequest(f"Invalid format : {format}")
response = HttpResponse(content=export_file.getvalue(), content_type=content_type)
response["Content-Disposition"] = "attachment; filename*=utf-8''{}".format(filename)
return response
# hottrack/urls.py
urlpatterns = [
# ...
# 정규표현식을 통해 csv/xlsx 확장자의 주소만 허용합니다. re_path(
route=r"^export\.(?P<format>(csv|xlsx))$", view=views.export, name="export"
),
]
출처
반응형
'BackEnd > Django, DRF' 카테고리의 다른 글
[Django] django-debug-toolbar (0) | 2024.06.11 |
---|---|
[Custom] path와 re_path, CustomConverter를 통한 url 정의하기 (0) | 2024.06.10 |
[Django] timezone 모듈과 datetime 모듈의 차이 & 사용법 (1) | 2024.06.04 |
[DRF] Django로 일간 및 주간 조회수 구현하기: 코드 예제 (0) | 2024.06.04 |
[JWT] Custom JWT 관련 middleware와 backend (0) | 2024.05.31 |