200423-200426_TIL

|

4월 23일

  • TIL을 주 단위로 바꿔보고자 한다. 매일 파일을 만들어 올리는 것도 좋긴한데, 가독성 면에서는 한 페이지에 주 단위로 보는것이 더 좋다고 생각한다. git으로는 매일 기록해서 잔디밭을 유지해볼 것이다.

  • 패스트캠퍼스 파이썬 심화 과정의 Class를 보고 있다. 예전에 생활코딩에서 공부는 해봐서 나름(?) 수월하게 이해하고 있다. 관건은 만들 때 활용하는 것이다.
    • 클래스 변수, 인스턴스 변수에 대해 다시 이해해봤다.
    • 인스턴스 메소드와 클래스 메소드, 스태틱 메소드에 대해서도 봤는데, 스태틱 메소드는 굳이 사용할 필요성을 못느끼겠다. 이또한 언제간 느끼겠지?
  • 패스트캠퍼스 장고 실정 수강 시작!

4월 24일

  • 패스트캠퍼스 파이썬 심화 Special Method와 Advanced List & Tuple을 수강하였다. 클래스를 더 유연히 다루려면 Special Method를 익혀야 함을 느꼈다. 또, namedtuple에 대해서도 봤는데, 내가 원하던 기능도 있어서 한 번 활용할 수 있도록 해봐야겠다. 제네레이터 부분은 아직 이해가 잘 안가서 더 봐야겠다.

4월 25일

학교 수업을 들었다. 온라인 강의지만 과제와 들을게 많아지니 귀찮아 진다.

4월 26일

넷플릭스를 보면서 쉬었다.

200421_TIL

|

오늘 한 일

  • 패스트 캠퍼스 django 기초 강의 완강하였다.
  • 블로그에 올리는데 계속 안올라가길래, 뻘짓하고 있던 찰나 메일로 계속 다음과 같은 내용으로 알림이 오던 것이다. jekyll의 Liquid tag 처리 중 에러가 나는 내용인 듯 하다. 구글링 써치 결과 코드에 {% %} 안에 rawendraw로 감싸줘야 해결이 된다. 내가 경험한 바로는 csrf_token과 extends등에서 나타났다. 따로 나타날때마다 기입해야겠다.
The page build failed for the `master` branch with the following error:

The tag `csrf_token` on line 191 in `_posts/Django/2020-04-20-django4.md` is not a recognized Liquid tag. For more information, see https://help.github.com/en/github/working-with-github-pages/troubleshooting-jekyll-build-errors-for-github-pages-sites#unknown-tag-error.

For information on troubleshooting Jekyll see:

  https://help.github.com/articles/troubleshooting-jekyll-builds

If you have any questions you can submit a request on the Contact GitHub page at https://support.github.com/contact?repo_id=159783182&page_build_id=175553972

느낀 점

  • 템플릿 언어끼리도 충돌이 일어날 수 있음을 알게 되었다.

내일 할일

  • 공부 계속하기

python심화 - Class Advanced

|

Python 심화 - Class Advanced

우리는 왜 class를 사용해야 할까

파이썬에서 자주 사용하는 dictionary 자료형을 예로 들어보자. 만약 내가 학생을 관련해 만든다면은 학생의 이름, 번호, 학년, 등 성적 등을 만들 수가 있다. 아래 처럼 말이다. 3명의 학생에 대해 정보를 만들었는데, 얼핏봐도 수가 늘어나면 관리하기간 힘들어 보인다. 하지만 클래스를 사용하면 좀 더 직관적이고 효율적으로 관리할 수 있게 되는 것이다.

딕셔너리 사용
students_dicts = [
    {'stduent_name' : 'Kim',
     'student_number' : 1,
     'student_grade': 1,
     'student_detail':
        {'gender': 'Male', 'score1':95, 'score2':88}
     },
    {'stduent_name' : 'Park',
     'student_number' : 2,
     'student_grade': 2,
     'student_detail':
        {'gender': 'Female', 'score1':15, 'score2':28}
     },
    {'stduent_name' : 'Lee',
     'student_number' : 3,
     'student_grade': 3,
     'student_detail':
        {'gender': 'Male', 'score1':15, 'score2':83}
     }
]
클래스 사용
# 클래스 구조
# 구조 설계 후 재사용성 증가, 코드 반복 최소화, 메소드 활용

class Student():
    def __init__(self, name, number, grade, details):
        self._name = name
        self._number = number
        self._grade = grade
        self._details = details

    def __str__(self):
        return 'str : {}'.format(self._name)
    def __repr__(self):
        return 'repr : {}'.format(self._name)

student1 = Student('kim', 1, 1, {'gender': 'Male', 'score1': 95, 'score2': 88})
student2 = Student('Park', 2, 2, {'gender': 'Female', 'score1': 77, 'score2': 92})
student3 = Student('Lee', 3, 3, {'gender': 'Male', 'score1': 99, 'score2': 100})


# 이후 리스트에다가 인스턴스들을 삽입해준다
students_list = []
students_list.append(student1)
students_list.append(student2)
students_list.append(student3)


print(students_list)

# 반복(__st__)
for x in students_list:
    print(x)

클래스 변수와 인스턴스 변수

  • 클래스 변수: 클래스에 선언한 변수, 인스턴스들 모두 공유하는 데이터일 때 사용하면 좋다.
  • 인스턴스 변수: 인스턴스들만의 고유한 데이터들이라 보면 될거 같다.
    예시

    ```python class Student: “”” 이것은 클래스를 설명하는 문서다. 클래스.doc() 사용하면 볼 수 있다. Student Class Author : Hong Date: 2020.04.21 “””

    # 클래스 변수 선언 student_count = 0

    def init(self, name, number, grade, details, email=None): # 인스턴스 변수, self와 함께있는 것들은 인스턴스 변수라 보면 될거 같다. self._name = name self._grade = grade self._number = number self._details= details self._name = name self._email = email

      Student.student_count += 1 # 클래스 변수를 통해 학생 카운트
    

    def str(self): return ‘str {}’.format(self._name)

    def repr(self): return ‘str {}’.format(self._name)

    def detail_info(self): print(‘Current Id: {}’.format(id(self))) print(‘Student Detail Info : {} {} {}’.format(self._name, self._email, self._details))

    def del(self): Student.student_count -= 1

Self 의미: 생성된 객체를 가리킨다.

studt1 = Student(‘Cho’, 2, 3, {‘gender’: ‘Male’, ‘score1’ : 11, ‘score2’ : 44}) studt2 = Student(‘Chang’, 4, 1, {‘gender’: ‘Feale’, ‘score1’ : 61, ‘score2’ : 74}, ‘st2@naver.com’)

ID 확인 - 서로 다른 객체임을 확인

print(id(studt1)) print(id(studt2))

print(studt1._name == studt2._name) print(studt1 is studt2) # id 값 비교하기

dir & dict 확인

dict로 먼저 보고 dir로 보기

__dict__는 인스턴스 내 속성값들만 보고, dir()은 상속받은 모든 속성들을 보여주기 때문이다!

print(dir(studt1)) print(dir(studt2))

print(studt1.dict) # 인스턴스 변수 값도 보여줌 print(studt2.dict) # 인스턴스 변수 값도 보여줌

Docstring - 주석보기

print(Student.doc) print()

실행

studt1.detail_info() studt2.detail_info()

에러

Student.detail_info()

Student.detail_info(studt1) Student.detail_info(studt2)

비교

print(studt1.class, studt2.class) print(id(studt1.class) == id(studt2.class))

print()

인스턴스 변수

직접 접근(PEP 문법적으로 권장 X)

studt1._name = ‘HAHAHA’ # 이런것을 권장않고, 메소드를 이용해 바꿀 수 있도록 하기 print(studt1._name, studt2._name) print(studt1._email, studt2._email)

클래스 변수

인스턴스도 접근 가능

print(studt1.student_count) print(studt2.student_count) print(Student.student_count)

print() print()

공유 확인

print(Student.dict) # student_count 보임 print(studt1.dict) # student_count 안보임. print(studt2.dict) # student_count 안보임.

인스턴스 네임스페이스에 없으면 상위에서 검색

즉, 동일한 이름으로 변수 생성 가능(인스턴스 검색 후 -> 상위(클래스 변수, 부모 클래스 변수))

del studt2

print(studt1.student_count) print(Student.student_count)



### 클래스 메소드 & 스태틱 메소드
인스턴스를 통하지 않고, 클래스에서 직접 호출할 때 사용하는 메소드이다.
- 클래스 메소드: 메소드 위에 @classmethod를 기입해준다. 단, 첫번째 매개변수로 cls로 입력해준다. 클래스 메서드는 정적 메서드처럼 인스턴스 없이 호출할 수 있다는 점은 같지만 클래스 메서드는 메서드 안에서 클래스 속성, 클래스 메서드에 접근해야 할 때 사용한다.

- 스태틱 메소드: 메소드 위에 @staticmethod를 기입해준다. 메서드의 실행이 외부 상태에 영향을 끼치지 않는 순수 함수(pure function)를 만들 때 사용한다. 정적 메서드는 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때 사용합니다.
```python
# Chapter01-3
# 파이썬 심화
# 클래스 메소드, 인스턴스 메소드, 스태틱 메소드

# 기본 인스턴스 메소드

class Student(object):
    """
    Student Class
    Author : Kim
    Date : 2020.04.23
    Descriptipn : Class, Static, Instance Method
    """

    # Class Variable
    tuition_per = 1.0

    def __init__(self, id, first_name, last_name, email, grade, tuition, gpa):
        self._id = id
        self._first_name = first_name
        self._last_name = last_name
        self._email = email
        self._grade = grade
        self._tuition = tuition
        self._gpa = gpa

    # Instance Method - 인자로 self가 들어가면 인스턴스 메소드라 생각하면 될듯
    def full_name(self):
        return '{}, {}'.format(self._first_name, self._last_name)

    # Instance Method
    def detail_info(self):
        return 'Student Detail Info : {}, {}, {}, {}, {}, {}'.format(self._id, self.full_name(), self._email, self._grade, self._tuition,
                                                                     self._gpa)
    # Instance Method
    def get_fee(self):
        return 'Before Tuition -> Id: {}, fee: {}'.format(self._id, self._tuition)
    # Instance Method
    def get_fee_calc(self):
        return 'After Tuition -> Id: {}, fee: {}'.format(self._id, self._tuition * Student.tuition_per)

    def __str__(self):
        return 'Student Info -> name: {} grade: {} email: {}'.format(self.full_name(), self._grade, self._email)

    # Class Method
    @classmethod
    def raise_fee(cls, per):
        if per <= 1:
            print('Please Enter 1 or More')
            return
        cls.tuition_per = per
        print('Succed! tuition increased!')

    # Class Method
    @classmethod
    def student_const(cls, id, first_name, last_name, email, grade, tuition, gpa):
        return cls(id, first_name, last_name, email, grade, tuition * cls.tuition_per, gpa)

    # Static Method
    @staticmethod
    def is_scholarship_st(inst):
        if inst._gpa >= 4.3:
            return '{} is a scholarship recipient'.format(inst._last_name)
        return 'Sorry. Not a scholarship recipient.'

# 학생 인스턴스
student_1 = Student(1, 'Kim', 'Sarang', 'student1@naver.com', '1', 400, 3.5)
student_2 = Student(2, 'Lee', 'Myungho', 'student2@daum.com', '2', 500, 4.3)

# 기본정보
print(student_1)
print(student_2)

print()

# 전체 정보
print(student_1.detail_info())
print(student_2.detail_info())

print()

# 학비 정보(인상 전)
print(student_1.get_fee())
print(student_2.get_fee())

print()

# 학비 인상(클래스 메소드 사용 전) - 직접 접근해서 제어하면 좋지 않
# Student.tuition_per = 1.2

# 학비 인상(클래스 메소드 사용)
Student.raise_fee(1.3)

# 학비 정보(인상 후)
print(student_1.get_fee_calc())
print(student_2.get_fee_calc())
print()
# 클레스 메소드 인스턴스 생성 실습
student_3 = Student.student_const(3, 'Park', 'Minji', 'Student3@gmail.com', '3', 550, 4.5)
student_4 = Student.student_const(4, 'Cho', 'Sunghan', 'Student4@gmail.com', '4', 600, 4.1)

# 전체 정보
print(student_3.detail_info())
print(student_4.detail_info())
print()

# 학생 학비 변경 확인
print(student_3._tuition)
print(student_4._tuition)
print()

# 장학금 혜택 여부(스테이틱 메소드 미사용)
def is_scholarship(inst):
    if inst._gpa >= 4.3:
        return '{} is a scholarship recipient'.format(inst._last_name)
    return 'Sorry. Not a scholarship recipient.'

print(is_scholarship(student_1))
print(is_scholarship(student_2))
print(is_scholarship(student_3))
print(is_scholarship(student_4))
print()
# 장학금 혜택 여부(스테이틱 메소드 사용)
print(Student.is_scholarship_st(student_1))
print(Student.is_scholarship_st(student_2))
print(Student.is_scholarship_st(student_3))
print(Student.is_scholarship_st(student_4))

print()

print(student_1.is_scholarship_st(student_1))
print(student_2.is_scholarship_st(student_2))
print(student_3.is_scholarship_st(student_3))
print(student_4.is_scholarship_st(student_4))

Django 실전 정리

|

패스트캠퍼스 Django 실전 정리

프로젝트 및 앱 구성

django-admin startproject fc_django
django-admin startapp fcuser
django-admin startapp product
django-admin startapp order

모델 정의 및 앱 등록

유저

from django.db import models

class Fcuser(models.Model):
    email = models.EmailField(verbose_name='이메일')
    password = models.CharField(max_length=64, verbose_name='비밀번호')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='등록날짜')

    def __str__(self):
      return self.email

    class Meta:
        db_table = 'fastcampus_fcuser'
        verbose_name = '사용자'
        verbose_name_plural = '사용자'

제품

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=256, verbose_name='상품명')
    price = models.IntegerField(verbose_name='상품가격')
    description = models.TextField(verbose_name='상품설명')
    stuck = models.IntegerField(verbose_name='재고')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='등록날짜')

    def __str__(self):
      return self.name

    class Meta:
        db_table = 'fastcampus_product'
        verbose_name = '상품'
        verbose_name_plural = '상품'

주문

from django.db import models

class Order(models.Model):
    fcuser = models.ForeignKey('fcuser.Fcuser', on_delete=models.CASCADE, verbose_name='사용자')
    product = models.ForeignKey('product.Product', on_delete=models.CASCADE, verbose_name='상품')
    quantity = models.IntegerField(verbose_name='수량')
    register_date = models.DateTimeField(auto_now_add=True, verbose_name='등록날짜')

    def __str__(name):
      return str(self.fcuser) + ' ' + str(self.product)

    class Meta:
        db_table = 'fastcampus_order'
        verbose_name = '주문'
        verbose_name_plural = '주문'

앱 등록 및 마이그레이션

# settings.py
INSTALLED_APPS += [
    'fcuser',
    'product',
    'order',
]

이후
python manage.py makemigrations
python manage.py migrate

Admin 사이트 등록

유저

from django.contrib import admin
from .models import Fcuser

class FcuserAdmin(admin.ModelAdmin):
    # 튜플로 인식시켜야함
    list_display = ('email', )

admin.site.register(Fcuser, FcuserAdmin)

상품

from django.contrib import admin
from .models import Product

class ProductAdmin(admin.ModelAdmin):
    # 튜플로 인식시켜야함
    list_display = ('name', 'price')

admin.site.register(Product, ProductAdmin)

주문

from django.contrib import admin
from .models import Order

class OrderAdmin(admin.ModelAdmin):
    # 튜플로 인식시켜야함
    list_display = ('fcuser', 'product')

admin.site.register(Order, OrderAdmin)

Class-based View

재사용성을 늘리기 위해 클래스 뷰를 사용.

로그인, 회원가입 페이지 생성

forms 파일 생성

from django import forms
from django.contrib.auth.hashers import check_password, make_password
from .models import Fcuser

class RegisterForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    re_password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호 확인'
    )

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')
        re_password = cleaned_data.get('re_password')

        if password and re_password:
            if password != re_password:
                self.add_error('password', '비밀번호가 서로 다릅니다.')
                self.add_error('re_password', '비밀번호가 서로 다릅니다.')
            else:
                fcuser = Fcuser(
                    email=email,
                    password=make_password(password)
                )
                fcuser.save()

class LoginForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')

        if email and password:
            try:
                fcuser = Fcuser.objects.get(email=email)
            except Fcuser.DoesNotExist:
                self.add_error('email', '아이디가 없습니다')
                return

            if not check_password(password, fcuser.password):
                self.add_error('password', '비밀번호를 틀렸습니다')
            else:
                self.email = fcuser.email

views에 클래스 뷰 클래스 생성

from django.shortcuts import render
from django.views.generic.edit import FormView
from .forms import RegisterForm, LoginForm

def index(request):
    return render(request, 'index.html', {'email': request.session.get('user')})

class RegisterView(FormView):
    template_name = 'register.html'
    form_class = RegisterForm
    success_url = '/'

class LoginView(FormView):
    template_name = 'login.html'
    form_class = LoginForm
    success_url = '/'

    def form_valid(self, form):
        self.request.session['user'] = form.email

        return super().form_valid(form)

Product 리스트 불러오기

ListView를 제공하기에 모델을 불러와서 템플릿에 적용해줄 수 있다.

views파일

from django.shortcuts import render
from django.views.generic import ListView
from .models import Product

class ProductList(ListView):
    model = Product
    template_name = 'product.html'
    # 템플릿 파일에서 object_list 대신 사용할 이름
    context_object_name = 'product_list'

html 파일


{% extends "base.html" %}
{% load humanize %}
{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <table class="table table-light">
            <thead class="thead-light">
            <tr>
                <th scope="col">#</th>
                <th scope="col">상품명</th>
                <th scope="col">가격</th>
                <th scope="col">등록날짜</th>
            </tr>
            </thead>
            <tbody class="text-dark">
            {% for product in product_list %}
                <tr>
                    <th scope="row">{{product.id}}</th>
                    <th scope="row">{{product.name}}</th>
                    <th scope="row">{{product.price|intcomma}} 원</th>
                    <th scope="row">{{product.register_date|date:'Y-m-d H:i'}}</th>
                </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>
</div>
{% endblock %}

숫자나 시간을 보기 편하게 바꿔주기 위해 humanize를 불러서 적용한다. 프로젝트 settigs파일에 humanize 앱을 추가해줘야 한다.

추가

INSTALLED_APPS += [
    'fcuser',
    'product',
    'order',
    'django.contrib.humanize',
]

제품 생성 페이지 만들기

forms 파일 생성

from django import forms
from django.contrib.auth.hashers import check_password, make_password
from .models import Fcuser

class RegisterForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    re_password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호 확인'
    )

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')
        re_password = cleaned_data.get('re_password')

        if password and re_password:
            if password != re_password:
                self.add_error('password', '비밀번호가 서로 다릅니다.')
                self.add_error('re_password', '비밀번호가 서로 다릅니다.')
            else:
                fcuser = Fcuser(
                    email=email,
                    password=make_password(password)
                )
                fcuser.save()

class LoginForm(forms.Form):
    email = forms.EmailField(
        error_messages={
            'required' : '이메일을 입력해주세요.',
        },
        max_length=64, label='이메일'
    )
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },
        widget=forms.PasswordInput, label='비밀번호'
    )
    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')

        if email and password:
            try:
                fcuser = Fcuser.objects.get(email=email)
            except Fcuser.DoesNotExist:
                self.add_error('email', '아이디가 없습니다')
                return

            if not check_password(password, fcuser.password):
                self.add_error('password', '비밀번호를 틀렸습니다')
            else:
                self.email = fcuser.email

views 파일

from django.shortcuts import render
from django.views.generic import ListView
from django.views.generic.edit import FormView
from .models import Product
from .forms import RegisterForm

class ProductList(ListView):
    model = Product
    template_name = 'product.html'
    # 템플릿 파일에서 object_list 대신 사용할 이름
    context_object_name = 'product_list'

class ProductCreate(FormView):
    template_name = 'register_product.html'
    form_class = RegisterForm
    success_url = '/product/'

html파일


{% extends "base.html" %}
{% block contents %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>상품 생성</h1>
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        {{ error }}
    </div>
</div>
<div class="row mt-5">
    <div class="col-12">
        <form method="POST" action=".">
            {% csrf_token %}
            {% for field in form %}
            <div class="form-group">
                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                {% ifequal field.name 'description'%}
                <textarea class="form-control" name="{{field.name}}" id="{{ field.id_for_label }}"></textarea>
                {% else %}
                <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}" />
                {% endifequal %}
            </div>
            {% if field.errors %}
            <span style="color: red">{{field.errors}}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">생성</button>
        </form>
    </div>
</div>
{% endblock %}

200420_TIL

|

오늘 한 일

  • 패스트 캠퍼스 django 기초강의를 들었다. (게시판 만들기)
  • 블로그로 공부했던 내용을 정리 중이다.

느낀 점

  • django를 공부하면서 파이썬의 부족한 지식을 느끼게 된다. 공부 공부!
  • 강의를 보거나 인터넷을 참조하면 다들 사용하는 메소드가 조금씩 달라서 헷갈릴 때가 많다. 일단은 계속 해보면서 체득해야 할거 같다.

내일 할일

  • 공부 계속하기