[Django]장고 복습 3

Django 복습 3️⃣


복습 2번에 이어서..

Detail(상세 내용)창 만들기

  • dbtest(App)의 index.html로 이동 후 내용변경

    • <td><a href="detail/{{data.id}}">{{ data.mytitle }}</a></td>
      
  • templates에 detail.html 생성 후 작성

    • <body>
          
          <h1>Detail</h1>
          
          <table border="1">
              <tr>
                  <th>작성자</th>
                  <td><input type="text" value="{{dto.myname}}" readonly></td>
              </tr>
              <tr>
                  <th>제목</th>
                  <td><input type="text" value="{{dto.mytitle}}" readonly></td>
              </tr>
              <tr>
                  <th>내용</th>
                  <td><textarea rows="10" cols="60" readonly>{{dto.mycontent}}</textarea></td>
              </tr>
              <tr>
                  <td colspan="2" align="right">
                      <input type="button" value="목록" onclick="">
                      <input type="button" value="수정" onclick="">
                      <input type="button" value="삭제" onclick="">
                  </td>
              </tr>
          
          </table>
          
      </body>
      
  • views.py로 이동 후 내용 추가

    • def detail(request, id): # id라는 변수로 값을 받아서 사용할 수 있게 도와준다.
          return render(request, 'detail.html', {'dto': MyBoard.objects.get(id=id)})
      
  • urls.py로 이동

    • path('detail/<int:id>', views.detail, name='detail')
      
    • detail/에 <int:id> 값이 넘어올 것이란 것

Insert(게시물 작성)창 만들기

  • templates에 insert.html 생성 후 작성

    • <body>
          
          <h1>Insert</h1>
          
          <form action="/insertres" method="post">
              <table border="1">
                  <tr>
                      <th>작성자</th>
                      <td><input type="text" name="myname"></td>
                  </tr>
                  <tr>
                      <th>제목</th>
                      <td><input type="text" name="mytitle"></td>
                  </tr>
                  <tr>
                      <th>내용</th>
                      <td><textarea rows="10" cols="60" name="mycontent"></textarea></td>
                  </tr>
                  <tr>
                      <td colspan="2" align="right">
                          <input type="button" value="취소" onclick="" />
                          <input type="submit" value="글작성" />
                      </td>
                  </tr>
              </table>
          </form>
          
      </body>
      
  • views.py로 이동 후 내용 추가

    • from django.shortcuts import render, redirect
      from django.utils import timezone
          
      def insert_form(request):
          return render(request, 'insert.html')
          
      def insert_res(request):
          myname = request.POST['myname']
          mytitle = request.POST['mytitle']
          mycontent = request.POST['mycontent']
          
          result = MyBoard.objects.create(myname=myname, mytitle=mytitle, mycontent=mycontent, mydate=timezone.now())
          if result:
              return redirect('index')
          else:
              return redirect('insertform')
      
  • urls.py로 이동 후 내용 추가

    • path('insertform/', views.insert_form, name='insertform'),
      path('insertres/', views.insert_res),
      
  • index.html의 글작성 button을 눌렀을 때 insert.html로 이동해 내용을 적고 거기서 글작성(submit)버튼을 누르면 index.html에 나타나게 만들어보자.

    • index.html 부분

      • <td colspan="4" align="right">
            <input type="button" value="글작성" onclick="location.href='/insertform'"/>
        </td>
        
      • index.html로 가서 글작성을 누르면 insert.html로 이동하는걸 확인할 수 있다.

      • image

      • 여기서 내용 작성하고 글작성 버튼을 누르면 런타임에러가 뜬다.

      • image
    • insert.html 부분

      • <form action="/insertres/" method="post">
        
      • 폼태그 머리부분에 action에 /를 추가하고 다시 내용을 작성하고 글작성을 누르면 403:Forbidden에러가 난다.

      • 152710890-352421e1-7dd2-4cc3-adaf-c28f32a8ca89

      • 내용을 확인해보면 CSRF 부분에 문제가 있는 것 같다.

      • 이 경우에 폼태그 머리부분 뒤에 {% csrf_token %}를 붙여준다.(장고 복습 2번 참고, form태그 사용하면 무조건 csrf_token 사용하자!!!)

      • <form action="/insertres/" method="post">{% csrf_token %}
        
      • 그리고 다시 작성해보면 잘 작성되는것을 확인할 수 있다.

      • image

      • image

Update(게시물 수정)창 만들기

  • detail.html의 수정버튼이 작동하게 만들어보자.

  • templates에 update.html 생성 후 작성

    • 기존에 테이블에 작성된 내용을 수정하기 위한 html이다.

    • <body>
          
          <h1>Update</h1>
          
          <form action="/updateres/" method="post">{% csrf_token %}
              <input type="hidden" name="id" value="{{dto.id}}" />
              <table border="1">
                  <tr>
                      <th>작성자</th>
                      <td><input type="text" name="myname" value="{{dto.myname}}" readonly /></td>
                  </tr>
                  <tr>
                      <th>제목</th>
                      <td><input type="text" name="mytitle" value="{{dto.mytitle}}"/></td>
                  </tr>
                  <tr>
                      <th>내용</th>
                      <td><textarea rows="10" cols="60" name="mycontent">{{dto.mycontent}}</textarea></td>
                  </tr>
                  <tr>
                      <td colspan="2" align="right">
                          <input type="button" value="취소" onclick="">
                          <input type="submit" value="수정">
                      </td>
                  </tr>
              </table>
          
          </form>
          
          
      </body>
      
    • myname은 글 작성자이기 때문에 readonly로 설정해 수정할 수 없게 해놓았다.

    • mytitle, mycontent는 수정할 수 있는 사안이기에 readonly를 적어주지 않고 수정이 가능하게 설정
  • views.py로 이동 후 내용 추가

    • def update_form(request, id):
          return render(request, 'update.html', {'dto':MyBoard.objects.get(id=id)})
          
      def update_res(request):
          id = request.POST['id']
          mytitle = request.POST['mytitle']
          mycontent = request.POSt['mycontent']
          
          myboard = MyBoard.objects.filter(id=id)
          
          result_title = myboard.update(mytitle=mytitle)
          result_content = myboard.update(mycontent=mycontent)
          
          if result_title + result_content == 2:
              return redirect('detail/'+id)
          else:
              return redirect('updateform/'+id)
      

참고 : 모델객체.objects.get()와 모델객체.objects.filter()의 차이

  • objects.get()
    • 검색 결과에 해당하는 하나의 객체만을 반환.
    • 유니크한 값을 가지고 검색하는 것을 추천
  • objects.filter()
    • 검색 결과에 해당하는 여러가지 객체를 포함하는 쿼리셋(QuerySet)을 반환
    • 키워드 검색으로 특정 조건을 만족하는 객체를 검색할 때 사용
  • urls.py로 이동 후 urlpatterns 안에 내용 추가

    • path('updateform/<int:id>', views.update_form, name='updateform'),
      path('updateres/', views.update_res),
      
  • image

    • 수정 버튼을 눌러도 아무 변화가 없다.(수정 페이지로 가져야하는데)
  • detail.html로 이동해 수정 버튼을 클릭했을 때 update페이지로 보내주는 링크를 작성해야한다.(js이용)

    • <input type="button" value="수정" onclick="location.href='/updateform/{{dto.id}}'">
      
    • update 앞에 /를 적어줌으로써 http://localhost:8000/updateform/id로 갈수 있게 된다.

    • 만약 /를 적지 않으면 /detail/updateform/id 이렇게 경로가 잡힌다.

    • {{dto.id}} : 값을 같이 가지고 가겠다는 의미

    • 다시 수정버튼 눌러서 확인

    • image

    • 잘 가지는 것을 확인
  • 이제 update페이지에서 제목과 내용을 수정하고 수정버튼을 눌렀을 때 detail에 반영되게 만들어보자.

    • 지금 상태에서 내용을 수정하고 수정버튼을 누르면 404에러가 난다.

    • image

    • 그 이유는 아까와 같이 경로에 문제가 있어서다.

    • views.py로 이동해 update_res의 내용을 고쳐주자.

      • def update_res(request):
            id = request.POST['id']
            mytitle = request.POST['mytitle']
            mycontent = request.POST['mycontent']
              
            myboard = MyBoard.objects.filter(id=id)
              
            result_title = myboard.update(mytitle=mytitle)
            result_content = myboard.update(mycontent=mycontent)
              
            if result_title + result_content == 2:
                return redirect('/detail/'+id)
            else:
                return redirect('/updateform/'+id)
        
      • 마지막 if/else 문의 redirect의 괄호 안의 경로의 맨 앞에 ‘/’ root를 잡아줘야 한다.
    • 다시 update 페이지로 넘어가서 내용 수정 후 수정버튼을 눌러보자.

    • 152715258-81a11a32-110e-47a7-b156-41c726909858

    • 경로와 내용이 잘 수정된 것을 확인할 수 있다.

Delete(게시물 삭제) 기능 만들기

  • 이번엔 detail페이지에서 삭제(delete)기능을 추가해보자.

    • views.py로 이동 후 내용 추가

      • def delete(request, id):
            result_delete = MyBoard.objects.filter(id=id).delete()
              
            if result_delete[0]:
                return redirect('index')
            else:
                return redirect('detail/'+id)
        
      • 요청한 id값과 일치하는 id값의 모델 객체를 삭제
    • urls.py로 이동 후 내용 추가

      • path('delete/<int:id>', views.delete),
        
    • detail.html로 이동해 삭제버튼 내용 수정

      • <input type="button" value="삭제" onclick="location.href='/delete/{{dto.id}}'">
        
    • 삭제 버튼 누르고 결과 확인

    • image

    • image


프로젝트를 MySQL과 연결후 다시 과정을 반복해보자.

  • 참고 :https://docs.djangoproject.com/en/4.0/ref/databases/#mysql-notes

  • 서버 종료 후 cd ..

  • myboard라는 프로젝트 생성

    • django-admin startproject myboard
    • image
  • pip install mysqlclient로 mysql 클라이언트 설치

    • (myweb) C:\Workspaces\workspace_django>pip install mysqlclient
      Collecting mysqlclient
        Downloading mysqlclient-2.1.0-cp39-cp39-win_amd64.whl (180 kB)
           |████████████████████████████████| 180 kB 1.6 MB/s
      Installing collected packages: mysqlclient
      Successfully installed mysqlclient-2.1.0
      
  • myboard(App)의 settings.py로 이동(MySQL을 위한 세팅을 해주자.)

    • INSTALLED_APPS에 ‘myboard’ 추가

      • INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'myboard',
        ]
        
      • 등록을 해놔야 models.py랑 DB가 연결된다고한다.
    • DATABASES의 내용 변경

      • DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',
                'NAME': 'mysql',
                'USER' : 'root',
                'PASSWORD' : '비밀번호~',
                'HOST' : 'localhost',
                'PORT' : '3306',
            }
        }
        
  • myboard(App)에 models.py 생성

    • from django.db import models
          
      class MyBoard(models.Model):
          myname = models.CharField(max_length=100)
          mytitle = models.CharField(max_length=500)
          mycontent = models.CharField(max_length=1000)
          mydate = models.DateTimeField()
          
          def __str__(self):
              return str({'myname': self.myname, 'mytitle': self.mytitle, 'mycontent': self.mycontent, 'mydate': self.mydate})
      
    • MyBoard라는 모델 객체를 만들어준다.
  • myboard(App)에 templates라는 폴더 생성 후 그 안에 index.html 생성

    • 원래 table로 레이아웃 짜면 안된다. divspan등 영역 태그와 CSS를 활용해야함.

    • <body>
          
          <h1>Hello, {{ request.session.myname | default:"Django" }} with mysql</h1>
              
          <table border="1">
              <col width="50">
              <col width="100">
              <col width="500">
              <col width="100">
              <tr>
                  <th>번호</th>
                  <th>작성자</th>
                  <th>제목</th>
                  <th>작성일</th>
              </tr>
              {% if not list%}
                  <tr>
                      <th colspan="4">-------- 작성된 글이 없습니다 --------</th>
                  </tr>
              {% else %}
                  {% for data in list %}
                      <tr>
                          <td>{{ data.id }}</td>
                          <td>{{ data.myname }}</td>
                          <td><a href="#">{{ data.mytitle }}</a></td>
                          <td>{{ data.mydate | date:"Y-m-d" }}</td>
                      </tr>
                  {% endfor %}
              {% endif %}
              <tr>
                  <td colspan="4" align="right">
                      <input type="button" value="글작성" onclick="">
                  </td>
              </tr>
          </table>
          
      </body>
      
    • 장고 documentation에서 문법 확인 : request.session.name | deafult: "django"

      • |는 템플릿 필터를 칭한다.
      • Session.세션이름 의 값이, default 값으로 ‘Django’로 설정
    • 번호, 작성자, 제목, 작성일이 컬럼으로 들어가는 테이블을 만들어준다.

    • 만약, {% if not list%} list가 없다면 작성된 글이 없다고 출력

    • list가 있다면 list안의 값들을 반복문을 통해 그 요소들의 id, myname, mytitle, mydate를 가져온다.

      • {{ data.mydate | date:'Y-m-d'}} : mydate의 날짜 형식을 설정
  • myboard(App)에 views.py 생성

    • from django.shortcuts import render
      from .models import MyBoard
          
      def index(request):
          return render(request, 'index.html', {'list': MyBoard.objects.all().order_by('-id')})
      
    • index라는 경로로 요청이 들어오면 index.html를 반환해주며, list에는 DB객체에 있는 모든 값을 내림차순으로 정렬해서 조회하고 저장해준다.

    • order_by 사용할때 ’-‘를 써주면 ASC->DEC
  • myboard(app)에 urls.py 로 이동

    • from django.contrib import admin
      from django.urls import path
      from . import views
          
      urlpatterns = [
          path('admin/', admin.site.urls),
          path('', views.index, name='index'),
      ]
      
    • path 설정해주고, 그 path의 이름을 index로 설정
  • 터미널 : cd myboard

  • 모델 객체 만들기 : python manage.py makemigrations myboard

  • 만들어진 모델 객체 데이터베이스에 적용 : python manage.py migrate

    • image
  • 서버 가동 후 확인: python manage.py runserver

    • image

Insert(게시물 작성) 창 및 기능 만들기

  • templates에 insert.html 생성

    • <textarea>는 여러 줄의 긴 문장을 입력할 수 있는 태그다.

      • col속성은 가로 크기, row속성은 세로 크기
    • <body>
          
          <h1>Insert</h1>
          
          <form action="/insertres/" method="post">{% csrf_token %}
              <table border="1">
                  <tr>
                      <th>작성자</th>
                      <td><input type="text" name="myname"></td>
                  </tr>
                  <tr>
                      <th>제목</th>
                      <td><input type="text" name="mytitle"></td>
                  </tr>
                  <tr>
                      <th>내용</th>
                      <td><textarea rows="10" cols="60" name="mycontent"></textarea></td>
                  </tr>
          
                  <tr>
                      <td colspan="2" align="right">
                          <input type="button" value="취소" onclick="">
                          <input type="submit" vlaue="글작성">
                      </td>
                  </tr>
              </table>
          </form>
          
      </body>
      
  • views.py로 이동

    • from django.shortcuts import render, redirect
      from .models import MyBoard
      from django.utils import timezone
          
      def insert_form(request):
          return render(request, 'insert.html')
          
      def insert_res(request):
          myname = request.POST['myname']
          mytitle = request.POST['mytitle']
          mycontent = request.POST['mycontent']
          
          result = MyBoard.objects.create(myname=myname, mytitle=mytitle, mycontent=mycontent, mydate=timezone.now())
          
          if result:
              return redirect('index')
          else:
              return redirect('insertform')
          
      
  • urls.py로 이동 후 urlpatterns안에 입력

    • path('insertform/', views.insert_form, name='insertform'),
      path('insertres/', views.insert_res),
      
  • templates의 index.html로 이동 후 글작성버튼을 눌렀을 때 onclick 속성 수정

    • <input type="button" value="글작성" onclick="location.href='/insertform/'">
      
  • 서버에서 확인

    • image

Detail(상세 내용)창 만들기

  • 상세정보창 만들기

  • index.html 에서 mytitle이 나오는 곳 <td>태그에 href경로를 잡아준다.

    • <td><a href="{% url 'detail' data.id %}">{{ data.mytitle }}</a></td>
      
  • templates에 detail.html 생성

    • <h1>Detail</h1>
          
          <table border="1">
              <tr>
                  <th>작성자</th>
                  <td><input type="text" value="{{dto.myname}}" readonly></td>
              </tr>
              <tr>
                  <th>제목</th>
                  <td><input type="text" value="{{dto.mytitle}}" readonly></td>
              </tr>
              <tr>
                  <th>내용</th>
                  <td><textarea rows="10" cols="60" readonly>{{dto.mycontent}}</textarea></td>
              </tr>
              <tr>
                  <td colspan="2" align="right">
                      <input type="button" value="목록" onclick="">
                      <input type="button" value="수정" onclick="">
                      <input type="button" value="삭제" onclick="">
                  </td>
              </tr>
          
          </table>
      
  • views.py로 이동 후 내용 추가

    • def detail(request, id):
          return render(request, 'detail.html', {'dto': MyBoard.objects.get(id=id)})
      
    • 각 게시물들의 id값을 받아줘야 그에 해당하는 templates에 적용가능
  • urls.py의 urlpatterns에 내용 추가

    • path('detail/<int:id>', views.detail, name='detail'),
      
  • 서버에서 확인

    • image

Update(게시물 수정) 창 및 기능 만들기

  • 수정(update)창 만들기

  • templates에 update.html 생성

    • <body>
          
          <h1>Update</h1>
          
          <form action="/updateres/" method="post">{% csrf_token %}
              <input type="hidden" name="id" value="{{dto.id}}" />
              <!-- 글 번호 값 안보이게-->
              <table border="1">
                  <tr>
                      <th>작성자</th>
                      <td><input type="text" name="myname" value="{{dto.myname}}" readonly /></td>
                  </tr>
                  <tr>
                      <th>제목</th>
                      <td><input type="text" name="mytitle" value="{{dto.mytitle}}"/></td>
                  </tr>
                  <tr>
                      <th>내용</th>
                      <td><textarea rows="10" cols="60" name="mycontent">{{dto.mycontent}}</textarea></td>
                  </tr>
                  <tr>
                      <td colspan="2" align="right">
                          <input type="button" value="취소" onclick="">
                          <input type="submit" value="수정">
                      </td>
                  </tr>
              </table>
          
          </form>
          
          
      </body>
      
    • <input type="hidden"> : 글 번호값 안보이게

    • {{ dto.값 }} : views.py에서 설정해준 ‘dto’의 값들

    • submit 버튼을 누르면 <form>태그 내의 name속성의 value값들을 action에서 정해준 경로(/updateres/)로 POST방식(method)으로 전달.
  • detail.html의 수정버튼의 onclick 속성 변경

    • <input type="button" value="수정" onclick="location.href='/updateform/{{dto.id}}'">
      
    • 버튼을 클릭하면 /updateform을 요청하는데, 그러면서 {{dto.io}}라는 값도 함께 요청
  • views.py에 내용 추가

    • # 글 번호에 맞는 detail들의 값들을 가져온다.
      def update_form(request, id):
          return render(request, 'update.html', {'dto':MyBoard.objects.get(id=id)})
          
          
      # 각 값을 받아서 수정해 templates에 보내주는 과정
      def update_res(request):
          id = request.POST['id']
          mytitle = request.POST['mytitle']
          mycontent = request.POST['mycontent']
          
          myboard = MyBoard.objects.filter(id=id)
          
          result_title = myboard.update(mytitle=mytitle)
          result_content = myboard.update(mycontent=mycontent)
          
          if result_title + result_content == 2: # 내가 원하는대로 동작했다면
              return redirect('/detail/'+id) # 디테일로 가고
          else: # 그게 아니라면
              return redirect('/updateform/'+id) # 직전으로 돌아가라
      
    • detail에서 요청했을 때 그 detail이 가지고 있는 번호, 제목, 내용 값을 가져와야한다.

    • id가 id와 같은 객체를 찾아 dto에 반환해서 보내준다.

    • objects.filter(id=id) : SQL의 WHERE 절 역할

    • result_? : queryset중 적어준 값과 같은 결과를 찾아서 .update()해줄 값 설정(SQL의 UPDATE의 SET 기능과 흡사)
  • urls.py의 urlpatterns에 내용 추가

    • path('updateform/<int:id>/', views.update_form, name='updateform'),
      path('updateres/', views.update_res),
      
    • path('updateres/')는 update.html에서 <form>태그의 action부분에서 그렇게 설정해줬기 때문에, submit 버튼을 누를 시에 views.update_res로 처리해달라고 요청한다.
  • 서버에서 확인

    • 작성자 창 옆은 readonly라 수정이 안되고 제목, 내용 창의 텍스트박스는 수정이 가능
    • image

Delete(게시물 삭제) 기능 만들기

  • 삭제 기능 만들기

  • detail.html 삭제 버튼 onclick 속성 변경

    • <input type="button" value="삭제" onclick="location.href='/delete/{{dto.id}}'">
      
  • views.py에 내용 추가

    • def delete(request, id):
          result_delete = MyBoard.objects.filter(id=id).delete()
      	# id가 같은 객체의 queryset을 delete()해주고 result_delete에 저장
          # print(result_delete)
          if result_delete[0]: # result_delete는 튜플형태(,)
              return redirect('index')
          else:
              return redirect('detail/'+id)
      
  • urls.py의 urlpatterns에 내용 추가

    • path('delete/<int:id>', views.delete),
      
  • 삭제 기능 확인

목록(Index)으로 돌아가게 만들기(목록 버튼)

  • detail.html에서 목록 버튼의 onclick 속성 변경

    • <input type="button" value="목록" onclick="location.href='/'">
      
    • /root를 뜻한다. 즉, index.html의 경로는 http://127.0.0.1:8000/로 잡혀있다. 그래서 경로에 /만 적어주면 index.html로 보내준다.
  • 상세 정보 페이지에서 목록버튼을 누르면 index페이지로 가는 것을 확인할 수 있다.

2022

[web]jQuery 복습 3

1 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]jQuery 복습 2

13 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]jQuery 복습 1

14 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]JavaScript 정리4

5 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]JavaScript 정리3

10 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]JavaScript 정리2

7 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]JavaScript 정리1

8 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]CSS 기초 정리

11 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[web]HTML 기초 정리

8 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

[Pandas]pandas 연습

3 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

맨 위로 이동 ↑

2021

[Python기초]module

1 분 소요

[Noitce] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

맨 위로 이동 ↑