본문 바로가기

Django 게시판 만들기 (4) - 글 수정/삭제 하기, 로그인 구현 본문

Programming/django

Django 게시판 만들기 (4) - 글 수정/삭제 하기, 로그인 구현

ksoes 2022. 4. 3. 13:25

 

 

  • 글 수정하기
    • detail.html에서 수정하기 버튼을 생성
      더보기
      <a href="{% url 'board:post_modify' post.id %}" class="btn btn-secondary" style="float:right">수정하기</a>
    • 수정할 글의 고유번호인 post_id를 인자로 주고 매핑이 namespace가 board이고 url별칭이 post_modify인 것을 찾는다
      더보기
      path('post/modify/<int:post_id>/', views.post_modify, name='post_modify'),
    • views.py에서 modify_post를 작성한다
      더보기
      def post_modify(request, post_id): # request:<WSGIRequest: GET '/board/post/modify/3/'>, post_id:3(세번째로 작성한 글을 수정하므로)
          """    글 수정하기    """
          post = get_object_or_404(Post, pk=post_id) # <class 'board.models.Post'> # model에서 Post클래스의 pk가 수정하려는 post의 번호인 객체(?)를 가져온다. # post.id, post.title, post.content, post.create_date 조회시 수정하려는 글(행)에 저장된 데이터가 들어있음
          if request.method == 'POST':  # ↓ 저장하기 눌러야 활성화 # print(request.method) -> GET or POST 반환
              form = Postform(request.POST, instance=post) # 폼들 만들되, post데이터를 사용하여 폼을 채운다. # <tr><th><label for="id_title">Title:</label></th><td><input type="text" name="title" value="두번 째 글 쓰기" maxlength="150" required id="id_title"></td></tr> <tr><th><label for="id_content">Content:</label></th><td><textarea name="content" cols="40" rows="10" required id="id_content">두번 째 글의 내용입니다</textarea></td></tr>
              if form.is_valid():
                  post = form.save(commit=False) # 커밋하지 않고 form에 있는 내용을 저장한다 post객체에 담아(?)
                  post.modified_date = timezone.now()
                  post.save() # db에 내용 저장 및 반영
                  return redirect('board:detail', post_id=post.id) # 수정한 글로 이동
          else:
              form = Postform(instance=post) # 인스턴스 키워드로 기존에 존재하는 모델 객체를 인수로 준다.
          context = {'form': form} # 템플릿에 넘길 폼 객체를 dict형태로 넘긴다
          return render(request, 'board/post_form.html', context)

 

  • 글 삭제하기
    • detail.html에서 삭제하기 버튼 생성 
      더보기
      <a href="{% url 'board:post_delete' post.id %}" class="btn btn-secondary" style="float:left">삭제하기</a>
    • url 매핑 작성
      더보기
      path('post/delete/<int:post_id>/', views.post_delete, name='post_delete'),
    • views.py에서 post_delete를 작성한다
      더보기
      def post_delete(request, post_id):
          """    글 삭제하기    """
          post = get_object_or_404(Post, pk=post_id) # post모델에서 삭제하려는 글의 id에 해당하는 레코드의 객체를 가져온다
          post.delete() # 가져온 레코드를 삭제한다
          return redirect('board:index')

 

  • 로그인 구현
    • python manage.py startapp accounts : 로그인 및 인증 기능을 담당할 app accounts 새로 생성
    • config/settings.py에 새로 생성한 accounts app을 등록한다
    • path('accounts/', include('accounts.url')), : ~account/~ 로 시작하는 url이 요청이 들어오면 accounts앱 쪽에서 처리하도록 config.urls.py에 등록
    • accounts/urls.py 파일 생성
      더보기
      app_name = 'accounts' 
      urlpatterns = [ ]
    • base.html의 navbar에서 로그인 href='#' 을 href={% url 'accounts:login' %} 으로 변경
      • namespace: accounts, url별칭: login
        • accounts의 login으로 연결되도록 매핑한다
    • 탬플릿 태그로 {% url 'accounts:login' %}를 사용했으므로, accounts.urls.py에 매핑규칙을 추가한다
      • 더보기
        from django.urls import path
        from django.contrib.auth import views as auth_views # 이 부분을 추가해야 django auth에 내장되어있는 LoginView와 LogoutView를 사용할 수 있다. 따라서 accounts앱에 따로 views.py에서 loginview, logoutview 코드를 작성할 필요가X(장고에 내장된 것을 임포트하여 가져다 쓰므로) # 앱 폴더 내에 있는 views.py와 충돌하지 않도록 별칭을 설정한다.
        
        app_name = 'accounts'
        
        urlpatterns = [
            path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), app_name='login'), # django\contrib\auth\views.py에 있는 클래스 LoginView를 가져다 쓴다.
            # as_view() : 클래스형 뷰에서 제공하는 클래스로 진입하기 위한 진입 메소드. View 클래스에 정의되어 있음. -> 여기서는 클래스형 view인 LoginView에 진입하기 위한 메소드라는 것. 함수view vs 클래스view : if문으로 http 메소드를 구분하는 함수view와 다르게, if문없이 함수로 코드구분을 하여 깔금하게 작성할 수 있다. 아래 주소 참고.
            # 참고 : http://ruaa.me/django-view/
            # as_view() 안에 인자는 LoginView는 regstration이라는 템플릿 디렉터리에서 login.html파일을 찾는다. 찾는 파일을 accounts디렉터리의 템플릿을 참조하도록 한 것.
        ]
    • template/common 디렉터리를 생성해 login.html 파일을 생성한다.
      • 더보기
        {% extends "base.html" %}
        {% block content %}
        <div class="container my-3">
            <form method="post" action="{% url 'accounts:login' %}">
                {% csrf_token %}
                {% include "form_errors.html" %}
                <div class="mb-3">
                    <label for="username">사용자ID</label>
                    <input type="text" class="form-control" name="username" id="username"
                           value="{{ form.username.value|default_if_none:'' }}">
                </div>
                <div class="mb-3">
                    <label for="password">비밀번호</label>
                    <input type="password" class="form-control" name="password" id="password"
                           value="{{ form.password.value|default_if_none:'' }}">
                </div>
                <button type="submit" class="btn btn-primary">로그인</button>
            </form>
        </div>
        {% endblock %}
      • 로그인에 사용되는 사용자ID 를 의미하는 username과 비밀번호를 의미하는 password 항목은 django.contrib.auth 앱이 요구하는 필수 항목. (필드는 여러개가 있다. username, password, first_name, email, is_staff, is_active 등, 참고 : https://docs.djangoproject.com/en/4.0/ref/contrib/auth/ )
    • templates/form_errors.html 파일을 작성한다
      • 더보기
        <!-- 필드 오류와 넌필드 오류를 출력한다. -->
        {% if form.errors %}
        <div class="alert alert-danger">
            {% for field in form %}
            <!-- 필드 오류 -->
            {% if field.errors %}
            <div>
                <strong>{{ field.label }}</strong>
                {{ field.errors }}
            </div>
            {% endif %}
            {% endfor %}
            <!-- 넌필드 오류 -->
            {% for error in form.non_field_errors %}
            <div>
                <strong>{{ error }}</strong>
            </div>
            {% endfor %}
        </div>
        {% endif %}
      • 필드 오류(field.errors) : 사용자가 입력한 필드 값에 대한 오류로, 값이 누락되거나 필드의 형식이 일치하지 않는 경우에 발생하는 오류이다.
      • 넌필드 오류(form.non_field_errors) : 필드의 값과 상관없이 다른 이유로 발생하는 오류이다.
    • config/settings.py에 로그인 성공 후 이동하는 URL을 추가한다.
      • 더보기
        # 로그인 성공후 이동하는 URL
        LOGIN_REDIRECT_URL =
        '/'
      • 로그인이 성공하면 django.contrib.auth 패키지는 디폴트로 /accounts/profile/이라는 url로 이동시키기 때문에, /profile/ 이 없는 대신 다른 페이지로 이동하라고 설정한 것이다.
    • board/urls.py에 '/'페이지에 대응하는 url매핑규칙을 추가한다
      • 더보기
        from board import views # 기존 내용에 추가
        
        urlpatterns = [
        	path('', views.index, name='index'), # 추가
        ]

 

 

  • 로그아웃 구현
    • 로그인을 했을 경우, 네비게이션바에 로그인이 로그아웃으로 변경되도록 base.html파일을 수정한다
      • 더보기
        <li class="nav-item ">
            {% if user.is_authenticated %} <! authetincated : 진짜임을 증명하다 >
            <a class="nav-link" href="{% url 'accounts:logout' %}">로그아웃</a>
            {% else %}
            <a class="nav-link" href="{% url 'accounts:login' %}">로그인</a>
            {% endif %}
        </li>
      • 템플릿에서 user 사용하기
        • 뷰 함수에서 템플릿에 user 객체를 전달하지 않더라도 템플릿에서는 django.contrib.auth 기능으로 인해 User객체를 사용할 수 있다. 대표적으로 다음과 같은 것들이 있다.
          • user.is_authenticated : 현재 사용자가 인증되었는지 여부 (로그인한 상태라면 true, 로그아웃 상태라면 false) authenticated(진짜임을 증명하다)
          • user.is_anonymous : is_authenticated의 반대경우, (로그인한 상태라면 false, 로그아웃 상태라면 true) anonymous(익명의)
          • user.username : 사용자명 (사용자ID)
          • user.is_superuser : 사용자가 슈퍼유저인지 여부
          • 참고 : https://docs.djangoproject.com/en/4.0/ref/contrib/auth/
    • accounts/urls.py에 logout url매핑도 추가한다
      • 더보기
        path('logout/', auth_views.LogoutView.as_view(), name='logout'),
    • 로그아웃 시 리다이렉트할 위치도 config/settings.py에 추가한다
      • 더보기
        # 로그아웃 성공후 이동하는 URL
        LOGOUT_REDIRECT_URL = '/'

 

 

 

Comments