오늘 학습 내용
[블로그] 상세페이지 만들기
블로그에 작성자 컬럼 만들기 및 오류 해결법
author = models.ForeignKey(User, on_delete=models.CASCADE)
- models.CASCADE => 같이 삭제
- models.PROTECT => 삭제가 불가능함
- 유저를 삭제하려고할때 블로그가 있으면 유저 삭제가 불가능
- models.SET_NULL => null값을 넣습니다.
- 유저 삭제시 블로그의 author가 null이 됨, 이 때 null=True 옵션도 함께 설정 필요
- User
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
- 2-1.
User = get_user_model()
- 항상
django.contrib.auth.models 여기에 있는 User을 사용하지 않기 때문에
- 2번을 사용하면 User을 새롭게 만들어야 할 경우 연결된 User이 어떤것인지 파악 후 가져옴
- 즉, 장고에 설정되있는 유저를 찾아서 가져오는 함수

- 모델의 수정사항이 발생하면,
python manage.py makemigrations
- null=True / degault 둘 중 한개가 필요한데 없어서 오류 발생
- 해결방법
- 1회성 디폴트 값을 기입 / 2. 추후 수동 처리
- 현재 실습 상황: 1번을 선택하고 디폴트 갑승로 1을 기입

- 즉, User테이블을 만들때 만들지 않았던 컬럼을 만들고 이 컬럼이 null이 불가능 할 경우

Base.html로 템플릿 extends 시키기
- Template Engine extends
- 전체페이지, 네비게이션 같은 공통된 템플릿을 공유
{% block content %}{% endblock %}
- 공통적으로 갖는 상단 네비바를 제외하고 자유롭게 변형할 수 있도록 설정

- Django Forms의 기능
- HTML에 input 그려주기
- validation (검증)
- 저장 및 업데이트
from django import forms
from blog.models import Blog
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
fields = ('title', 'content', )
class BlogForm(forms.ModelForm):
- django의 forms를 가져와서 그 forms안에 있는 ModelForm을 상속받음
fields
- 어떤 컬럼을 forms에 적용할지 지정
- 전체 적용:
__all__ / 특정 컬럼: list , dic

코드 설명
from blog.forms import BlogForm
def blog_create(request):
form = BlogForm(request.POST or None)
if form.is_valid():
blog = form.save(commit=False)
blog.author = request.user
blog.save()
return redirect(reverse('blog_detail', kwargs={'pk': blog.pk}))
context = {'form': form}
return render(request, 'blog_create.html', context)
- form = BlogForm(request.POST or None)
- BlogForm: Django의 ModelForm을 기반으로 한 폼 클래스
- 사용자가 POST 요청(글 작성 폼 제출)을 하면 request.POST 데이터를 이용해 폼을 채움
- GET 요청(페이지 처음 접속)은 request.POST가 없으므로 None이 들어가 빈 폼이 표시
- if form.is_valid():
- 사용자가 입력한 데이터가 폼의 유효성 검사(예: 빈칸, 글자 수 제한 등)를 통과했는지 확인
- True면 아래 저장 로직이 실행, 아니면 마지막의 return render(...)로 다시 폼을 보여줌
- blog = form.save(commit=False)
- 폼의 데이터를 기반으로 Blog 모델 인스턴스를 만들지만 DB에는 아직 저장하지 않음을 의미
- commit=False 덕분에 DB 저장 전에 추가로 author를 지정할 수 있게 됨
- blog.author = request.user
- 현재 로그인한 사용자를 author 필드에 연결
- 즉, 새로 작성된 글의 작성자를 현재 로그인한 사용자로 지정
- blog.save()
- 실제로 DB에 저장, 이제 새 블로그 글이 생성되고 blog.pk(기본키, ID)가 생김
- return redirect(reverse('blog_detail', kwargs={'pk': blog.pk}))
- 저장 후, 새로 작성된 글의 상세 페이지로 리다이렉트
- reverse('blog_detail', kwargs={'pk': blog.pk})
- 'blog_detail'이라는 URL 패턴에 pk 값을 넣어서 URL 문자열을 만들어 줌(ex. /blog/5/)
context = {'form': form}
return render(request, 'blog_create.html', context)
- 폼이 유효하지 않거나 처음 접속한 경우,
- blog_create.html 템플릿을 렌더링해서 사용자에게 폼을 보여줌
- context에 form을 담아 템플릿에서 {{ form.as_p }}처럼 사용 가능
로그인 하지 않은 유저가 작성 할 경우 로그인 페이지로 보내버리기

- 똑같은 기능을 데코레이터로 이용해보기(@login_required())
- 로그인 페이지를 지정해주지 않아도 자동으로 지정됨
- settings의 LOGIN_URL =
'/accounts/login/'
http://127.0.0.1:8000/accounts/login/?next=/create/
- 다음페이지가 어딘지도 나옴
next=/create/

로그인 해도 next뒤에 있는 페이지로 안가짐
def login(request):
form = AuthenticationForm(request, request.POST or None)
if form.is_valid():
django_login(request, form.get_user())
next = request.GET.get('next')
if next:
return redirect(next)
return redirect(reverse('blog_list'))
else:
form = AuthenticationForm(request)
context = {
'form': form
}
return render(request, 'registration/login.html', context)
- next = request.GET.get('next')
request.GET['next'] 도 가능하지만 이것 없으면 오류발생
http://127.0.0.1:8000/login/?next=/create/ | '?' 뒤에있는 next를 받는 것
최근 글이 가장 위로 올라오도록 코드를 수정

-created_at : DESC / created_at : ASC
블로그 목록 페이지에서 바로 글을 작성 연결

블로그 수정페이지 만들기
- 수정버튼은 로그인 유저와 글을 작성한 유저가 동일할 때만 보여야 함
- 작성자와 로그인한 유저가 동일해야 수정 버튼이 활성화

블로그 상세페이지에도 수정 버튼을 추가
- if request.user == blog.author
- 이 조건 없으면 아무한테나 다 수정버튼이 보이게 됨
- instance=blog
- 블로그 인스턴스를 넣어주면 폼의 항목에 맞게 타이틀,컨텐츠 등등 구분이 자동으로 들어감

블로그 페이지네이션
- 이미 있던 글을 여러번 반복해서 만듬

10개씩 나누기

페이지 하단에 페이지번호 링크를 추가하는 코드


특정 페이지로 이동하는 코드를 추가

- if page_object.has_previous
- 이전 페이지가 있는 경우, 첫 번째 페이지와 이전 페이지로 이동할 수 있는 링크를 보여줌
- if page_object.has_next
- 다음 페이지가 있는 경우, 다음 페이지와 마지막 페이지로 이동할 수 있는 링크를 보여줌
블로그 검색

q = request.GET.get('q')
if q:
blogs = blogs.filter(
Q(title__icontains=q) |
Q(content__icontains=q)
)
블로그 삭제하기
if request.method == 'POST':
raise Http404
@require_http_methods(['POST'])
@require_http_methods(['POST'])
- 특정 요청만 허락하는 데코레이터. 삭제나 수정은 POST 요청으로 받아야함
- 삭제 요청은 꼭 POST요청으로 받아야 함 / GET는 안됨
- POST요청 = csrf_token 필요


어려운 내용
새로 배운 내용
- django에서 post를 보내기 위해서는 csrf_token 꼭 필요
- 같은 의미의 코드지만 단순화

오늘 발생한 문제(발생 했다면)

- 문제: NOT NULL constraint failed: blog_blog.author_id
- 원인: blog.author 필드가 null이면 안 되는데 null이 들어가서 DB 저장에 실패했다는 뜻
- 이유: blog = form.save() (Blog 모델의 author 필드가 NOT NULL)
- commit=False 없이 바로 저장하고 있음 → 이 경우 BlogForm에 포함된 필드만 저장
- author는 form에 포함되어 있지 않음
- 즉, author_id=None 상태가 되어 DB에 저장하려다가 에러
blog = form.save(commit=False)
blog.author = request.user
blog.save()
- 해결
- form으로 객체는 만듬 / 2. DB에 저장하기 전에(author 지정 전) 잠시 멈추고
- blog.author = request.user 로 author 값을 채운 뒤 / 4. blog.save() 로 저장
- author_id=None → DB 저장 에러가 안 생김
- 즉, commit=False 덕분에 DB저장을 미루고 author 값을 넣고 save() 했기 때문
문제 2. 번호를 누르는 페이지네이션은 잘 되는데 다음 이전 버튼이 안됨
- 문제 3. 검색창까지는 잘 나오지만 검색이 안됨

- 원인: blogs = blogs.filter 를 설정 안했음