[Django]장고 복습 4

Django 복습 4️⃣


페이지네이션

  • 게시판과 같은 목록이 주어질 때, 모든 글을 불러올 수 없으니, 페이지 당 몇 개의 글을 보여줄 것인지 지정하는 모듈
  • 참고 : https://docs.djangoproject.com/en/4.0/topics/pagination/

  • views.py로 이동

    • # 모듈 호출
      from django.core.paginator import Paginator
          
      def index(request):
          myboard = MyBoard.objects.all().order_by('-id')
          # 페이지 당 몇개의 글을 보여줄 것인지 설정(5개)
          paginator = Paginator(myboard, 5)
          # 현재 페이지, 만약 없으면 1로 가져옴
          page_num = request.GET.get('page','1')
          
          # 페이지에 맞는 모델 가져오기(objects들을 queryset 형태로 반환)
          page_obj = paginator.get_page(page_num)
              
          # 관련 메서드
          print(type(page_obj)) # <class 'django.core.paginator.Page'> 
          print(page_obj.count) # 총 객체수
          print(page_obj.paginator.num_pages) # 총 페이지 개수
          print(page_obj.paginator.page_range) # 페이지의 범위
          print(page_obj.has_next()) # 다음 페이지가 존재 하는지 Bool
          print(page_obj.has_previous()) # 이전 페이지가 존재 하는지 Bool
          try:
              print(page_obj.next_page_number()) # 다음 페이지 num
              print(page_obj.previous_page_number()) # 이전 페이지 num
          except:
              pass
          print(page_obj.start_index()) # 시작 페이지(1)일 때는 제외
          print(page_obj.end_index()) # 마지막 페이지일 때는 제외
              
          return render(request, 'index.html', {'list': page_obj})
      
    • 관련 메서드 뭐하는 애들인지 찾아서 주석달기
  • index.html로 이동

    • 테이블 밑에 페이징

    • <body>
          <!--추가-->
          <!--처음으로-->
          <a href="?page=1">처음</a>
          <!--이전 페이지-->
          <!-- 만약 이전 페이지가 있다면 '이전'에 이전 페이지로 가는 링크-->
          {% if list.has_previous %}
              <a href="?page={{ list.previous_page_number }}">이전</a>
          <!-- 만약에 이전페이지가 존재하지않는다면(첫 페이지) 링크x-->
          {% else %}
              <a>이전</a>
          {% endif %}
          
          <!--페이징-->
          <!-- 페이지 객체가 담겨져 있는 list의 페이지 범위만큼 반복-->
          {% for page_num in list.paginator.page_range %}
          	<!-- 만약 페이지 숫자가 list의 숫자와 같다면 page_num에 강조처리-->
              {% if page_num == list.number %}
                  <b>{{ page_num }}</b>
          	<!-- 아니라면 본인이 가진 숫자의 페이지로 가는 링크를 가진 숫자로 -->
              {% else %}
                  <a href="?page={{ page_num }}">{{ page_num }}</a>
              {% endif %}
          {% endfor %}
              
          <!-- 다음 페이지 -->
          <!-- 만약 다음 페이지가 존재 한다면 '다음'에 다음페이지의 링크 설정-->
          {% if list.has_next %}
              <a href="?page={{ list.next_page_number }}">다음</a>
          <!-- 그게 아니라면 그냥 심심한 '다음' -->
          {% else %}
              <a>다음</a>
          {% endif %}
          <!-- 끝으로 -->
          <!-- paginator.num_pages는 총 페이지 개수를 가져오는데, 그 수는 마지막 페이지 번호와 같다. 그래서 '끝'이라는 내용에 <a>태그를 사용해 끝으로 가는 링크를 설정-->
          <a href="?page={{ list.paginator.num_pages }}"></a>
      </body>
      
  • python shell 실행 : python manage.py shell

  • 파이썬 쉘을 이용한 게시물 100개 생성

    • (InteractiveConsole)
      >>> from myboard.models import MyBoard
      >>> from django.utils import timezone
      >>> for i in range(1, 101):
      ...     temp = MyBoard(myname=i, mytitle=i, mycontent=i, mydate=timezone.now())
      ...     temp.save()
      >>> exit()
      
  • 다시 서버 실행후 확인

    • image

회원가입 기능 만들기

  • python manage.py createsuperuser : 모든 권한을 가진 계정 생성

    • username 지정
    • 이메일 지정
    • 패스워드 지정
  • myboard(App) 밑에 admin.py 생성

    • from django.contrib import admin
      from .models import MyBoard
          
      admin.site.register(MyBoard)
      
  • 서버 가동 후 127.0.0.1:8000/admin/으로 접속

    • 아까 지정한 username과 패스워드로 접속
    • image
  • Myboard로 가면 내가 만들어 놓은 테이블을 출력해준다.

    • image
    • DBMS를 사용하지 않아도 데이터들을 확인할 수 있게 장고 자체적으로 만들어놓았다.
  • 데이터를 수정할 수도 있다.

    • image
  • settings.py를 가서 장고 자체적으로 app으로 지정해놓은 것을 볼 수 있다.

    • image
  • models.py 로 이동

    • 모델 객체 정의

    • class MyMember(models.Model):
          myname = models.CharField(max_length=100)
          mypassword = models.CharField(max_length=100)
          myemail = models.CharField(max_length=100)
          
          def __str__(self):
              return str({'myname': self.myname, 'mypassword': self.mypassword, 'myemail': self.myemail})
      
  • python manage.py makemigrations myboard로 모델 객체 만들어주기

  • admin.py로 이동

    • from django.contrib import admin
      from .models import MyBoard, MyMember
          
      admin.site.register(MyBoard)
      admin.site.register(MyMember)
      
  • templates 폴더에 register.html 생성

    • <body>
          
          <h1>Register</h1>
          
          <form action="/register/" method="post">{% csrf_token %}
              NAME : <input type="text" name="myname">
              <br>
              <!-- 원래는 type을 password로 지정해줘야한다 -->
              PASSWORD : <input type="text" name="mypassword">
              <br>
              EMAIL : <input type="text" name="myemail">
              <br>
              <input type="submit" value="회원가입">
          </form>
          
      </body>
      
    • <form>태그를 만들 때는 항상 옆에 csrftoken을 적어줘야한다.
  • views.py로 이동

    • # MyMember 모듈 추가
      from .models import MyBoard, MyMember
      # 비밀번호를 암호화 시키는 모듈
      from django.contrib.auth.hashers import make_password
          
      def register(request):
          if request.method == "GET":
              return render(request, 'register.html')
          elif request.method == "POST":
              myname = request.POST['myname']
              mypassword = request.POST['mypassword']
              myemail =  request.POST['myemail']
          		
              # make_password를 통해 hasher된(암호화) mypassword가 들어감
              mymember = MyMember(myname=myname, mypassword=make_password(mypassword), myemail=myemail)
              # DB에 저장
              mymember.save()
          
              return redirect('/')
          
          return redirect('/')
          
      
  • urls.py로 이동 후 경로 추가

    • path('register/', views.register),
      
  • index.html로 이동 후 내용 추가

    • <body>
          <!-- 회원가입 -->
          <a href="register/">회원가입</a>
          <br>
          <!-- 로그인 -->
          {% if not request.session.myname %}
          <a href="login/">로그인</a>
          {% else %}
          <a href="logout/">로그아웃</a>
          {% endif %}
      </body>
      
  • 서버에서 확인

    • image
    • 회원가입 창에 들어가서 회원가입
    • image
    • http://127.0.0.1:8000/admin/ 으로 가서 My members를 확인!
    • image
      • 적어준 대로 데이터가 저장된 것을 확인할 수 있고,
      • password는 hash처리를 해서 가입할 때 설정한 1234와 다르게 여러 문자가 복잡하게 섞여서 나오는 것을 확인할 수 있다.

로그인 기능 만들기

  • templates에 login.html 만들기

    • <body>
          
          <h1>login</h1>
          
          <form action="" method="post">{% csrf_token%}
              NAME : <input type="text" name="myname">
              <br>
              <!-- 원래는 type을 password로 해야함 -->
              PASSWORD : <input type="text" name="mypassword">
              <br>
              <input type="submit" value="로그인">
          </form>
          
      </body>
      
  • views.py 로 이동

    • 모듈 호출(check_password)

      • from django.contrib.auth.hashers import make_password, check_password
        
    • def 추가

      • def login(request):
            if request.method == "GET":
                return render(request, 'login.html')
            else:
                myname = request.POST['myname']
                mypassword = request.POST['mypassword']
              		
                # myname이 myname과 같은 객체 한 개를 가져와 mymember에 저장
                mymemeber = MyMember.objects.get(myname=myname)
              		
                # 만약 패스워드가 mymember의 패스워드와 같다면(복호화)
                if check_password(mypassword, mymemeber.mypassword):
                    # mymember의 myname이 request.session의 myname속성에 저장
                    request.session['myname'] = mymemeber.myname
                    return redirect('/')
                else:
                    return redirect('/login')
        
  • urls.py로 이동 후 path 추가

    • path('login/', views.login),
      
  • 서버에서 확인

    • 로그인 화면
      • image
    • 로그인 후 index페이지
      • image
      • <h1>Hello, {{ request.session.myname | default:"Django" }}</h1> 이 부분이 로그인한 myname으로 바뀌어 출력된 것을 확인할 수 있다.
      • 로그인이 된 상태이기 때문에 원래 로그인이라 적혀있던 부분이 로그아웃으로 바뀐 것을 확인할 수 있다.

로그아웃 기능 만들기

  • views.py 로 이동

    • def 추가

      • def logout(request):
            del request.session['myname']
            return redirect('/')
        
  • urls.py로 이동 후 path 추가

    • path('logout/', views.logout),
      
  • 서버에서 확인

    • 로그인 된 상태에서 로그아웃 클릭
      • image-20220208132849740
    • 로그아웃 확인
      • image
  • 구글 크롬의 시크릿 모드를 통해서 확인

    • 시크릿모드는 개인에 관련된 정보를 저장하지 않는다. 그래서 로그인이 안된 상태인 것
    • image

Session이란?

  • 서버 하나에는 여러 클라이언트가 접근할 수 있다.
  • 서버는 각 클라이언트의 요청을 Thread로 처리한다.
    • Thread란?
    • Program : 실행파일
    • Process : 프로그램이 실제로 실행 됨 = 객체
    • Thread : 작업(실제로 일하는)
  • 그걸 처리하기 위한 서버의 객체가 session

  • 서버에서는 어떤 클라이언트가 접근하는지 그 정보를 session에 가지고 있다.
    • 로그인시 입력한 정보(id, password 등)를 session에 저장해놓는다.
  • session은 각 클라이언트마다 따로 존재해 클라이언트가 필요로하는 정보를 서버는 session에 담아논다.
    • 예를 들어, 네이버 로그인 후 접속정보부분에 메일, 카페 등 그 계정만의 정보를 나타내준다.
    • image

Django에서 제공하는 forms 기능 사용법

  • 새 프로젝트(mymember) 생성

    • django-admin startproject mymember
  • mymember(App)의 settings.py로 이동

    • INSTALLED_APPS에 ‘mymember’ 추가

      • INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'mymember',
        ]
        
    • TEMPLATES에 ‘DIRS’ 변경

      • 'DIRS': [BASE_DIR/'templates'],
        
    • DATABASES에 mysql적용

      • DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',
                'NAME': 'mysql',
                'USER': 'root',
                'PASSWORD': 'seri1453@@',
                'HOST': 'localhost',
                'PORT': '3306',
            }
        }
        
  • mymember(Project)에 templates 폴더 생성

  • templates에 index.html 생성

    • <body>
          
          <h1>Mymemeber</h1>
          
          <a href="register/">회원가입</a>
          <br>
          <a href="login/">로그인</a>
          
      </body>
      
  • mymember(App)에 forms.py 생성

    • 장고가 기본적으로 제공하는 로그인 폼을 사용해볼 것이다.

    • 모듈 호출

      • from django import forms
        from django.contrib.auth.forms import UserCreationForm
        from django.contrib.auth.models import User
        
      • UserCreationForm이 가진 기본적인 필드 : username, password1, password2

      • password1 : 비밀번호

      • password2 : 비밀번호 확인
    • 클래스 생성

      • class MyMemeberForm(UserCreationForm):
            # username, password1,2는 기본 포함, 그 외것들도 추가
            email = forms.EmailField()
            first_name = forms.CharField()
            last_name = forms.CharField()
              
            class Meta:
                model = User
                fields = ('username', 'password1', 'password2', 'first_name', 'last_name', 'email')
        
  • templates 밑에 register.html 생성

    • <body>
          
          <h1>Register</h1>
          
          <form actioin="." method="post">{% csrf_token %}
              <table>
                  <!-- form 형태를 테이블 형태로 가져온 것-->
                  {{ form.as_table }}
              </table>
              <input type="submit" value="회원가입">
          </form>
          
      </body>
      
  • mymember(App)에 views.py 생성

    • from django.shortcuts import render, redirect
      from .forms import MyMemeberForm
          
          
      def index(request):
          return render(request, 'index.html')
          
      def register(request):
          if request.method == "POST":
              form = MyMemeberForm(request.POST)
              if form.is_valid():
                  form.save()
                  return redirect('index')
          
          else:
              return render(request, 'register.html', {'form': MyMemeberForm()})
      
  • urls.py로 이동

    • 모듈 추가

      • from . import views
        
    • path 추가

      • urlpatterns = [
            path('admin/', admin.site.urls),
            path('', views.index, name='index'),
            path('register/', views.register, name='register'),
        ]
        
  • cd mymember : 경로 를 mymember 프로젝트로 잡아주고
  • python manage.py makemigrations mymember : 모델 객체 생성
  • python manage.py migrate : DB에 전달

  • python manage.py runserver : 서버 가동 후 확인
    • index 페이지
      • image
    • 회원가입을 누르면 밑의 화면으로 이동된다.
      • 밑에 화면처럼 만들어 준 적이 없다.
      • 장고가 지원해주는 UserCreationForm 를 사용해 만들어준 화면이다.
      • register.html에서 작성해준 것 처럼 {{ form.as_table }} 테이블 형태로 폼을 불러왔다.
      • image
  • 조건에 맞게 입력해서 회원가입을 해보자.
    • image
  • http://127.0.0.1:8000/admin/ 관리자 사이트에서 로그인
    • image
  • Authentication and Authorization의 Users를 클릭하면 다음 화면이 나온다.
    • image
  • templates에 login.html 생성

    • <body>
          
          <form action="{% url 'login' %}" method="post">{% csrf_token %}
              ID : <input type="text" name="username">
              <br>
              <!-- 원래 PW input type은 password로 해줘야한다.-->
              PW : <input type="text" name="password">
              <br>
              <input type="submit" value="로그인">
          </form>
          
      </body>
      
  • templates에 result.html 생성

    • <body>
          
          <h1>Hello, {{ user.username }}</h1>
          
          <a href="/logout/">로그아웃</a>
          
      </body>
      
  • settings.py로 이동

    • # login / logout
      LOGIN_REDIRECT_URL = '/result'
      LOGOUT_REDIRECT_URL = '/'
      
  • urls.py로 이동

    • 모듈 호출

      • from django.contrib.auth import views as auth_views
        
    • path 추가

      • path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
        path('logout/', auth_views.LogoutView.as_view(), name='logout'),
        path('result/', views.result, name='result'),
        
  • views.py로 이동

    • def 추가

      • def result(request):
            return render(request, 'result.html')
        
  • 서버에서 확인

    • 아까 만든 goodjeon 계정으로 로그인하면 다음과 같이 나온다.
      • image

파일 업로드/다운로드 기능

  • 프로젝트 생성

    • django-admin startproject updown
  • updown(App) settings.py 열기

    • TEMPLATES에서 DIRS 설정

      • 'DIRS': [BASE_DIR/'templates'],
        
    • 내용 추가

      • # media file upload / download
        MEDIA_URL = '/media/' # 경로
        MEDIA_ROOT = BASE_DIR/'media' # 폴더
        
      • 장고에서 일반적인 파일들을 media file이라고 관리한다. 그래서 경로와 폴더를 잡아줬다.
  • updown(Project) 밑에 templates 폴더와 media 폴더 생성

    • image
  • templates 밑에 index.html 생성

    • <body>
          
          <h1>File Upload</h1>
          
          <form action="/upload/" method="post" enctype="multipart/form-data">{% csrf_token %}
              <input type="file" name="uploadfile">
              <br>
              <input type="submit" value="업로드">
          </form>
      </body>
      
    • <form>태그의 enctype에는 3가지 종류가 있다.

    • 참고 : http://tcpschool.com/html-tag-attrs/form-enctype

      • application/x-www-form-urlencoded : 기본값으로, 모든 문자들은 서버로 보내기 전에 인코딩됨을 명시함.
      • multipart/form-data : 모든 문자를 인코딩하지 않음을 명시함. 이 방식은 <form> 요소가 파일이나 이미지를 서버로 전송할 때 주로 사용함.
      • text/plain : 공백 문자(space)는 “+” 기호로 변환하지만, 나머지 문자는 모두 인코딩되지 않음을 명시함.
    • 파일 하나를 추가할 건데, 파일 하나를 submit 하면 views.py로 갈 것이다.
  • views.py 생성

    • 모듈 호출

      • from django.shortcuts import render
        from django.core.files.base import ContentFile
        from django.core.files.storage import default_storage
        
    • def 생성

      • def index(request):
            return render(request, 'index.html')
              
        def upload_process(request):
            # 전달된 파일을 받아서
            upload_file = request.FILES['uploadfile']
            # print(upload_file)
            # print(type(upload_file))
              	
            # 받은 파일을 저장
            # default_storage가 settings.py에서 잡아준 media root
            uploaded = default_storage.save(upload_file.name, ContentFile(upload_file.read()))
            # print(uploaded)
            # print(type(uploaded))
              	
            # 요청에 따라 download.html로 보내주고 저장된 파일도 filename로 감싸서 보낸다.
            return render(request, 'download.html', {'filename': uploaded})
        
  • urls.py로 이동

    • 모듈 호출

      • from . import views
        
    • path 추가

      • path('', views.index, name='index'),
        path('upload/', views.upload_process),
        
  • 서버를 열어 파일을 보내보자.

    • 카카오톡.exe를 선택
      • image
    • download.html 을 만들지 않았기에 에러페이지가 나온다.
      • image
    • 하지만, media 폴더를 확인해보면 업로드 된것을 확인할 수 있다.
      • image
  • templates에 download.html을 생성

    • <body>
          
          <h1>File Download</h1>
          
          <input type="button" value="다운로드" onclick="location.href='/download/{{ filename }}'">
          
      </body>
      
  • views.py로 이동

    • 모듈 호출

      • from django.http import HttpResponse
        
    • def 추가

      • def download_process(request, filename):
            response = HttpResponse(default_storage.open(filename).read())
            response['Content-Disposition'] = f"attachment; filename={filename}"
              
            return response
        
  • urls.py로 이동 후 path 추가

    • path('download/<str:filename>', views.download_process),
      
  • 서버에서 다운로드 되는 것을 확인

    • image

참고

  • 혹시나 파일이름이 한글일 때는 언어 인코딩 설정을 변경해줘야한다.

    • settings.py 에서 LANGUAGE_CODE 변경

      • LANGUAGE_CODE : ‘en-us’ -> ‘ko-kr’

        • LANGUAGE_CODE = 'ko-kr'
          
  • 그리고 게시판에서 게시물을 작성하고 나면 작성 시간이 실제와 다르게 나와있는데, 한국 기준 시간에 맞춰줘야 한다.

    • settings.py 에서 TIME_ZONE 변경

      • TIME_ZONE : UTC -> ‘Asia/Seoul’

        • TIME_ZONE = 'Asia/Seoul'
          

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] 고쳐야하거나 틀린 것이 있으면 말씀해주세요!

맨 위로 이동 ↑