- poetry add django-summernote
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path
from blog import cb_views
# summernote 가 이미지를 업로드 해주거나 할때 들어갈 경로
path('summernote/', include('django_summernote.urls')),
# 실제로 배포환경에서 media의 경로가 달라질거여서 debug 가 필요해서 사용
if settings.DEBUG: # STATIC 처럼 디폴트 값으로 설정되어 있지 않아서 이렇게 연결 해주어야 함
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
from django_summernote.admin import SummernoteModelAdmin
@admin.register(Blog)
class BlogAdmin(SummernoteModelAdmin):
summernote_fields = ['content']
inlines = [ # 블로그 안에서 만들수 있께 해줌
CommentInline
]
<p>{{ blog.content | safe }}</p>
를 통해 content에 safe를 입히면 이미지가 출력이 된다.<!-- todo 에선 -->
{% if key == "description" %}
{{ value|safe }}
{% else %}
{{ value}}
{% endif}
# fields = ['catagory', ...]
form_class = BlogForm
from django_summernote.widgets import SummernoteWidget
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
# 어떤필드를 적용 시킬
fields = ('category', 'title', 'content') # Blog 모델의 'title'과 'content' 필드만 폼에 포함
# Meta 클래스는 ModelForm의 메타 데이터를 정의하는 부분입니다. 이 클래스는 ModelForm이 어떤 모델과 연결되는지, 어떤 필드를 폼에 포함할지 등의 정보를 제공
# fields = '__all__' # 전체를 다 적용하고 싶을때
widgets = {
'content' : SummernoteWidget()
}
# summernote
SUMMERNOTE_CONFIG = {
# Using SummernoteWidget - iframe mode, default
# Or, you can set it to `False` to use SummernoteInplaceWidget by default - no iframe mode
# In this case, you have to load Bootstrap/jQuery sources and dependencies manually.
# Use this when you're already using Bootstrap/jQuery based themes.
'iframe': True,
# You can put custom Summernote settings
'summernote': {
# As an example, using Summernote Air-mode
'airMode': False,
# Change editor size
'width': '100%',
'height': '480',
# Use proper language setting automatically (default)
# Toolbar customization
# https://summernote.org/deep-dive/#custom-toolbar-popover
'toolbar': [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['fontname', ['fontname']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link', 'picture', ]],
['view', ['fullscreen', 'help'],],
],
# Or, explicitly set language/locale for editor
'lang': 'ko-KR',
# You can also add custom settings for external plugins
'codemirror': {
'mode': 'htmlmixed',
'lineNumbers': 'true',
# You have to include theme file in 'css' or 'css_for_inplace' before using it.
'theme': 'monokai',
},
},
# Require users to be authenticated for uploading attachments.
'attachment_require_authentication': True,
# You can completely disable the attachment feature.
'disable_attachment': False,
# Set to `False` to return attachment paths in relative URIs.
'attachment_absolute_uri': True,
# test_func in summernote upload view. (Allow upload images only when user passes the test)
# https://docs.djangoproject.com/en/2.2/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin
# You can add custom css/js for SummernoteWidget.
}
In [1]: blog = Blog.objects.get(id=151)
In [2]: blog.content
blog.content = '필요한 부분'
blog.save() # 로 바꿀수 있음
- poetry add Pillow
- Django 내장라이브러리가 아니기에 installed app에 추가하지 않아도 된다.
image= models.ImageField('이미지', null=True, blank=True, upload_to='blog/%Y/%m/%d') #파일 저장시 /년 별로/달 별로/일 별로 저장됨
# 순서대로 읽기에 image 위치 선정
fields = ('category', 'title','image' ,'content') # Blog 모델의 'title'과 'content' 필드만 폼에 포함
# is_valid 후에 사용가능
def form_valid(self, form):
print(form.cleaned_data) # 폼 데이터를 출력
return super().form_valid(form) # 기본 동작 수행
enctype='multipart/form-data'
를 추가해 주어야 한다.<form method="POST" enctype="multipart/form-data">
{% if blog.image %}
<img src="{{ blog.image.url }}" alt="Blog Image" style="max-width: 100%; height: auto;">
{% endif %}
{% if blog.image %}
<img src="{{ blog.image.url }}" class="col-2">
{% endif %}
<span class="col-10">
({{ blog.get_category_display }}) {{ blog.title }} <span>{{ blog.author.username }}</span> - {{ blog.created_at|date:"Y-m-d" }}
</span>
- 목록에서 보이는 이미지 제작, 해당 이미지의 용량 줄이기
1.models.py thumbnail 추가, migrate
# null은 db에 들어갈때 의미하고 blank 는 form 에서 비어도 되는지안되는지 의미한다.
thumbnail = models.ImageField('썸네일', null=True, blank=True, upload_to='blog/%Y/%m/%d/thumbnail')
from PIL import Image # Pillow 에 있는 모듈
from pathlib import Path
from io import BytesIO
def save(self, *args, **kwargs):
if not self.image: # 이미지가 없으면 해당과정이 필요없어서 종료
return super().save(*args, **kwargs)
image = Image.open(self.image) # 이미지를 열고
image.thumbnail((300,300)) # 원하는 이미지로 조절
image_path = Path(self.image.name) # Path 라이브러리를 통해 이미지 경로 가져오기
thumbnail_name = image_path.stem # 확장자를 제외한 파일 이름
thumbnail_extension = image_path.suffix.lower() # 확장자만 가져옴
thumbnail_filename = f'{thumbnail_name}_thumb{thumbnail_extension}' # database_thumb.png 로 될거임, 원본파일과의 이름을 구분짓기 위해 사용
if thumbnail_extension in ['.jpg','jpeg']:
file_type = 'JPEG'
elif thumbnail_extension == '.gif':
file_type = 'GIF'
elif thumbnail_extension == '.png':
file_type = 'PNG'
else:# 지원하지않는 확장자면 그냥 종료
return super().save(*args, **kwargs)
# 바로 저장하면 좋지만 날짜나 이런걸 실수할수도 있다 그래서 어딘가로 메모리에 들고있다가
# 메모리에 있는걸 장고의 파일에 있는척 하면서 세이브 시켜줄겅임
temp_thumb = BytesIO()
image.save(temp_thumb, file_type) # 이미지를 지정한 파일 형식으로 메모리에 저장
temp_thumb.seek(0)
self.thumbnail.save(thumbnail_filename, temp_thumb, save= False) # Django의 FileField/ImageField를 사용해 썸네일을 모델에 저장.
temp_thumb.close() # 메모리 자원을 해제.
return super().save(*args, **kwargs)
# BytesIO 사용 이점
• 이미지를 바로 파일 시스템에 저장하지 않고, BytesIO라는 메모리 기반 객체에 임시로 저장합니다.
• 파일을 저장하고 다시 읽어오는 디스크 작업을 생략하고, 메모리 내에서 즉시 조작이 가능합니다.
실제 저장하는곳
self.thumbnail.save(thumbnail_filename, temp_thumb, save=False)
이곳에서 파일 시스템을 사용해 데이터를 저장한다
데이터는 이미 메모리에서 준비되었기에 I/O 비용을 최소화함
{% if blog.image %}
<img src="{{ blog.image.url }}" class="col-2">
{% endif %}
<!-- 수정 -->
{% if blog.thumbnail %}
<img src="{{ blog.thumbnail.url }}" class="col-2">
{% elif blog.image %}
<img src="{{ blog.image.url }}" class="col-2">
{% endif %}
# 이함수를 모델에 추가해놓으면
def get_thumbnail_image_url(self):
if self.thumbnail:
return self.thumbnail.url
elif self.image:
return self.image.url
return None
{% if blog.get_thumbnail_image_url %}
<img src="{{ blog.get_thumbnail_image_url }}" class="col-2">
{# {% if blog.thumbnail %}#}
{# <img src="{{ blog.thumbnail.url }}" class="col-2">#}
{# {% elif blog.image %}#}
{# <img src="{{ blog.image.url }}" class="col-2">#}
{% endif %}
- poetry add django-cleanup
- installed_app 에 등록만 해주면 imagefield나 filefield 가 사라지거나 등록될떄
- 경로에서 자동으로 삭제 해준다.
from django.conf import settings # 세팅 부를때 이렇게 부르는거 추천 환경변수가 바뀌어도 안변하고 settings 로 들어감
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, doucument_root=settings.MEDIA_ROOT)
from django_summernote.widgets import SummernoteWidget
widgets = {
'content' : SummernoteWidget()
from PIL import Image # Pillow 에 있는 모듈
from pathlib import Path
from io import BytesIO
def save(self, *args, **kwargs):
if self.image:
image = Image
image_path = Path(self.image.name)
temp_thumb = BytesIO()
queryset
• 정의: 데이터베이스로부터 가져온 객체들의 집합입니다. 즉, 특정 모델의 여러 인스턴스를 포함하는 컬렉션입니다.
• 주로 사용되는 곳: get_queryset() 메서드에서 사용됩니다.
• 역할:
• 다수의 객체를 가져올 때 사용됩니다.
• 필터링, 정렬 등의 작업을 통해 원하는 데이터의 서브셋을 가져오는 데 유용합니다.
• 예를 들어, 블로그의 댓글 전체를 가져오거나 작성자가 특정 유저인 블로그만 가져오는 데 사용됩니다.
self.object
• 정의: 특정 단일 객체를 나타냅니다. 즉, 모델의 한 인스턴스입니다.
• 주로 사용되는 곳: get_object() 메서드나 DetailView, UpdateView, DeleteView 등에서 사용됩니다.
• 역할:
• 주로 하나의 객체를 다룰 때 사용됩니다.
• 예를 들어, 블로그 상세 페이지나 댓글 수정 페이지에서 해당 블로그 또는 댓글의 객체를 가져올 때 사용됩니다.
특징 | queryset | self.object |
---|---|---|
내용 | 객체들의 집합 (다수) | 단일 객체 (하나) |
사용 위치 | get_queryset() 또는 ListView | get_object() 또는 DetailView , UpdateView |
사용 예 | 댓글 목록, 블로그 목록 가져오기 | 특정 블로그, 댓글, 사용자의 객체 가져오기 |
역할 | 데이터의 여러 개의 집합을 조작하거나 필터링 | 데이터베이스에서 단일 객체를 검색 및 반환 |
기반 클래스 | QuerySet | 특정 모델의 인스턴스 (Model 클래스 객체) |
텍스트
파일
숫자
날짜, 시간
연결
기타
class Payment(mdoels.Mdoel):
payment_response = models.JSONField(default = {},[])
기본적으로 get_object는 pk 또는 slug 값을 URL에서 가져와서, 뷰에 연결된 모델의 객체를 데이터베이스에서 검색하여 반환합니다.
def get_object(self, queryset=None):
return get_object_or_404(Blog, pk=self.kwargs['blog_pk'])
1. self.kwargs: # 기본적으로 url 에서 받아온거네 그걸담고있는게 저거고
• URLconf에 정의된 매개변수(예: <int:blog_pk>)를 담고 있습니다.
• self.kwargs['blog_pk']는 URL에서 blog_pk라는 키로 전달된 값을 가져옵니다.
# 그래서 기존의 pk 로된 것을 불러오는 것이 아닌 blog_pk를 가져오는거네
# 기본적으로 Django는 pk 또는 slug를 기준으로 객체를 가져옵니다. 그런데 지금 URLconf에서 blog_pk라는 키를 사용하고 있기 때문에 기본 get_object는 이를 인식하지 못합니다. 그래서 아래처럼 get_object_or_404를 사용해 명시적으로 Blog 객체를 가져오도록 오버라이드한 것입니다.
기본적으로 is_valid 이후에만 사용이 가능하며 제대로 전달이 됬나 확인 할 수 있음
- 클래스 기반 뷰에서 제공되는 메서드로 CreateView,UpdateView 에서 사용된다. 폼의 데이터가 유효한 경우에 호출되며 기본적으로 폼데이터를 저장하고 성공적으로 처리된 응답을 반환.
form_valid는 폼이 유효한 데이터를 가지고 있는 상태에서 호출됩니다. 따라서 이 메서드 자체가 유효성을 판단하는 것은 아니지만, 폼이 유효한 상태에서 추가 작업을 하거나 기본 동작을 커스터마이징할 수 있는 지점입니다.
폼의 유효성 판단은 이미 form.is_valid() 단계에서 이루어졌고, 이 메서드는 그 다음 단계에서 동작합니다.
# 사용방법 2가지
# 1. 저장전에 데이터추가
def form_valid(self, form):
self.object = form.save(commit=False) # 데이터를 저장하지 않고 객체 생성
self.object.author = self.request.user # 현재 사용자 추가
self.object.save() # 저장
return super().form_valid(form)
# 저장후 추가작업
def form_valid(self, form):
response = super().form_valid(form) # 기본 저장 작업
send_email_to_admin(form.cleaned_data) # 저장 후 추가 작업 수행
return response