TIL

23.04.06 TIL

best_spear_man 2023. 4. 7. 01:51

오늘 한 것

* git 특강 수강(git branch, git stash, git revert, git reset)(링크)

* 코딩테스트 풀이-zip()의 활용 (완주하지 못한 선수, 행렬의 곱셈)(링크)

* 개인과제 시작

 

4월 6일엔 개인과제를 하면서 다양한 에러와 문제들을 마주하였다. 오?늘의 TIL에는 그런 것들과 씨름한 기록을 남긴다.

 

 0 - 1 . form

장고의 form은 다양한 장점을 가지고 있으며 장고가 생산성 높은 프레임워크가 되게하는 중요한 요소중 하나이다. (다른하나는 모델)

폼은 올바른 데이터만 처리할 수 있게 검증기능을 제공하며, 모델폼 기능을 통해 모델과 동일한 필드를 가지는 폼을 자동 생성할 수 있다.

Model Fields가 DB Field 들 파이썬 클래스화한다면, Form Fields는 HTML Form Field 들을 파이썬 클래스화한다.

이를 통해 HTML을 쉽게 작성할 수 있다. 또한 폼 필드를 통해 사용자로부터 다양한 종류의 입력을 API로 받아올 수 있다.

 

0 - 2. ERD

과제를 수행하기 위해 실제로 ERD를 작성해 보았다.

ERD

대략적인 데이터 구조는 위와 같이 표현 할 수 있었다.

1. 상품과 창고 재고는 1대1 관계이다.

2. 출고와 입고 데이터는 창고재고와 1대 다  관계이다.

문제 1. no such table: django_session 에러

문제상황: 장고서버를 실행 시키자 마자 위와 같은 에러가 발생했다.

해결:잘 읽어보니 특정 데이터의 테이블이 존재하지 않는다는 의미였다. 따라서

python3 manager.py makemigrations
python3 manager.py migrate

위와 같은 명령어를 통해 ORM을 잘 반영하니 쉽게 해결됐다.

 

문제 2.  AuthenticationForm.is_valid() 가 항상 False 반환

문제상황:

def sign_in_view(request):
	...
    elif request.method == 'POST':
        sign_in_form = AuthenticationForm(request.POST)
        if sign_in_form.is_valid():
            auth.login(request, sign_in_form.get_user())
            return redirect('/')
        return render(request, 'accounts/signup.html', {'form': sign_in_form})

DB에 유저를 등록하고 위와 같이 views.py에 로그인을 위한 함수를 작성한 뒤 로그인을 시도하자, 분명 정확히 입력하였는데도 계속해서 유효하지 않은 입력으로 처리되었다.

 

시도 1: cleaned_data 속성 쓰기

쟝고에서 폼을 이용한 로그인 기능 구현을 검색해보자, form 객체의 cleaned_data 속성으로 부터 원하는 데이터를 가져오는 방식을 사용하는 경우가 많았다.

 

cleaned_data는 폼이 입력받은 값중 유효한 입력값 만을 키-밸류 형태로 저장중인 속성이다. is_valid()와 함께 사용해 오류를 방지하고 사용하는 경우가 많았으므로 입력값이 계속 유효하지 않은 이 상황에서는 적절치 않을 수 있으나 그냥 해봤다.

그랬더니 의외의 결과가 나타났는데, 폼 자체가 cleaned_data를 가지고 있지 않았다.

 

시도 2: form 안 쓰기

이쯤 되니 머리가 아프기 시작해서 그냥 일단 강의에서 배운대로 form을 쓰지않고 로그인을 구현해보기로 했다.

def sign_in_view(request):
	...
    elif request.method == 'POST':
        username = request.POST.get('username', None)
        pw = request.POST.get('password', None)

        me = auth.authenticate(request, username=username, password=pw)
        if me is not None:
            auth.login(request, me)
            return redirect('/')
        else:
            return redirect('/sign-in/')

위 코드는 폼을 사용하지 않고 POST 데이터로부터 직접 일일히 입력값을 받아와 auth 모듈의 authenticate와 login을 사용해 로그인을 진행하는 방식이다.
작동하긴 했지만, form을 이용하는 것의 간결함이 아른거려서 다시 한번 해법을 찾아보기로 했다.

 

해결: 인자를 빼먹었었다.

계속 찾다보니 다음과 같이 작성한 이 있었다.

from django.contrib.auth import login as auth_login
from django.contrib.auth.forms import AuthenticationForm

def login(request):
	if request.method == 'POST':
        form = AuthenticationForm(request, request.POST)
       
        if form.is_valid():
            auth_login(request, form.get_user())
            return redirect('/')

내가 작성한 것과는 다르게 request가 인자로 들어있는 것을 확인 할 수 있다.

class AuthenticationForm(forms.Form):
    """
    Base class for authenticating users. Extend this to get a form that accepts
    username/password logins.
    """

    username = UsernameField(widget=forms.TextInput(attrs={"autofocus": True}))
    password = ...
    ...
    def __init__(self, request=None, *args, **kwargs):

클래스의 선언을 찾아보니, 생성자 메서드에서 request를 인자로 받고 이후 다른 인자들을 받아오는 형식으로 되어있음을 확인했다.

지금까지 필수로 넣어야하는 request를 넣지 않아 제대로 폼이 형성되지 않고 오류가 발생했던 것이었다.

def sign_in_view(request):
	...
    elif request.method == 'POST':
        sign_in_form = AuthenticationForm(request,request.POST)
        if sign_in_form.is_valid():
            auth.login(request, sign_in_form.get_user())
            return redirect('/')
        return render(request, 'accounts/signup.html', {'form': sign_in_form})

위와 같이 수정하니 잘 작동하였다.

 

문제 3. '.gitignore' 미적용

문제상황:

gitignore에 다음과 같이 작성하였는데도 일부 pyc파일과 db.sqliete3 파일이 계속 추적되는 상태에 있었다.

venv/
### Django ###
*.log
*.pot
*.pyc
__pycache__/
db.sqlite3
db.sqlite3-journal

시도 1. 해당 파일을 따로 gitignore에 추가

계속 추적되고 있는 일부 파일들을 따로 gitignore에 추가해보았으나 여전히 추적되고 있었다.

 

해결. git 캐시 삭제

이러한 문제는 git 이 캐시에 저장해두는 것 때문에 생겼던 것이었다. 아래와 같이 캐시를 삭제하고, 변경사항들을 다시 스테이지에 올려서 커밋하면 해결되었다.

git rm -r --cached .
git add .
git commit -m "fixed untracked files"

문제4.  장고-템플릿으로 불러온 폼 내용 변경

help text들 꼬라지를 보라. 어휴 지저분한거

<form method="post">
    {% csrf_token %}

    {{form.as_p}}

    <input type="submit" name="submit" value="회원가입"/>
  </form>

위와 같이 템플릿을 작성하자, 사진과 같이 지저분하게 help_text들이 난무하였다. 이것들을 없애고 싶었다.

 

시도 1. 조상 클래스를 찾아 어디서 저 템플릿을 만들어오는지 확인하기

UserCreationForm의 조상 중 누가 어디서 html파일을 가져와 저렇게 만들어놓는지 확인해보고, 가능하다면 html파일을 가져오는 부분을 오버라이딩 해서 해결하려고 했다.

조상을 찾아보니 django/forms/forms.py 안에 위치한 BaseForm에 해당 템플릿들의 주소가 지정되어 있었다.

class BaseForm(RenderableFormMixin):
    """
    The main implementation of all the Form logic. Note that this class is
    different than Form. See the comments by the Form class for more info. Any
    improvements to the form API should be made to this class, not to the Form
    class.
    """
	...

    template_name_div = "django/forms/div.html"
    template_name_p = "django/forms/p.html"
    template_name_table = "django/forms/table.html"
    template_name_ul = "django/forms/ul.html"
    template_name_label = "django/forms/label.html"

좋다꾸나 해서 django/forms/p.html 를 찾아가 봤더니

음... 완벽하게 이해했어!!

아직 내가 이걸 다룰 단계는 아닌것 같다.

 

해결: help_text 변경

form을 이루는 각 필드에는 help_text가 달려 있다. 이것을 None으로 바꿔버리면 아무것도 출력하지 않게되므로 원하는 바를 이룰 수 있다.

class SignupForm(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'email',]

    def __init__(self, *args, **kwargs):
        super(UserCreationForm, self).__init__(*args, **kwargs)

        for fieldname in ['username', 'password1']:
            self.fields[fieldname].help_text = None

__init__()을 오버라이딩해 원치않는 필드의 help_text들을 None으로 바꾸어 주었다.

 

성공

잘 된다.

 

문제 5.  모델의 필드에 선택 리스트 적용

옷 사이즈를 입력할 때 일일히 S,M,L,XL,Free를 입력하고 싶은 사람은 별로 없을 것이다. 모델의 특정 필드에 값을 직접 입력하는 것이 아니라 선택지중 고르게 만들고 싶었다.

 

해결:

# model
class Product(models.Model):
	...
    sizes = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
        ('F', 'Free'),
    )
    size = models.CharField(choices=sizes, max_length=1)

금방 답을 찾을 수 있었는데, 필드에 choices 키워드 인자를 주면 된다. 인자에는 튜플의 리스트나 튜플의 튜플이 들어가면 된다. [0]에는 실제 데이터베이스에 저장될 이름이 들어가고, [1]에는 사용자에게 보여질 이름이 들어간다.

 

오늘 배운 것

1. 폼이 얼마나 유용한지 몸소 깨달았다

2. makemigration ,migrate 잘하자

3. 클래스를 상속해올 때는 조상까지 인자를 잘 보도록 하자. AuthenticationForm은 request인자를 반드시 필요로 한다.

4. gitignore가 정상작동안할땐 캐시삭제를 한번 해보자

5. 이미 정의된 클라스 등을 수정해서 사용하고싶을 땐 조상들을 잘 찾아보고 오버라이딩할 부분을 찾자.

레퍼런스

[Python, Django] Model의 필드에 선택리스트 적용 간단히 해보기

 

[Python, Django] Model의 필드에 선택리스트 적용 간단히 해보기

[Python, Django] Model의 필드에 선택리스트 적용 간단히 해보기 MODEL의 특정 필드에 값을 직접 입력하는게 아니라 선택을 하게 처리 하는 방법 선택을 위한 항목을 작성 LANGUAGE_ENGLISH = "en" LANGUAGE_KOREAN

fenderist.tistory.com

https://niceman.tistory.com/114

 

Git - 캐시(Cache) 삭제 방법 및 상세 설명

Git - Cache 삭제 설명 Git을 사용한 프로젝트 진행 중에 크리티컬한 문제는 아니지만, 신경쓰이는 두 가지 상황이 발생했습니다. 첫 번째 경우는 프로젝트와 관련 없는 private 종류의 폴더를 원격 저

niceman.tistory.com

https://kimmeh1.tistory.com/233

 

[Django 09] Authentication

폼을 통해 게시글을 작성할 때 제목과 내용, 작성시간, 수정시간 등을 데이터베이스의 모델과 연관된 ModelForm을 생성하여 활용하였다. ModelForm을 통해 쉽게 폼을 입력받고 데이터베이스에 저장할

kimmeh1.tistory.com

https://engineer-mole.tistory.com/304

 

[Django] Form의 cleaned_data 그리고 is_valid()시 호출되는 메소드의 순서

※ 일본의 블로그 글을 번역한 포스트입니다. 오역 및 직역, 의역이 있을 수 있으며 틀린 내용이 있으면 지적해주시면 감사하겠습니다. Django의 Form 관련 코드를 보면 종종 cleaned_data가 기재되어

engineer-mole.tistory.com

https://stackoverflow.com/questions/13202845/removing-help-text-from-django-usercreateform

 

Removing help_text from Django UserCreateForm

Probably a poor question, but I'm using Django's UserCreationForm (slightly modified to include email), and I would like to remove the help_text that Django automatically displays on the HTML page....

stackoverflow.com