상세 컨텐츠

본문 제목

피로그래밍14기: day9 <DJANGO Troubleshooting>

환 codes web/DJANGO

by 퍼블리셔환 2021. 1. 17. 09:08

본문

 

 

0. 헤어나올 수 없는 장고의 매력

 

영화 감독들은 각자의 뮤즈를 그린다. 신인감독과 신인배우가 둘 모두의 청춘을 태워서 영화를 만들다 보면 서로를 누구보다 더 잘 이해하는건 어찌보면 당연하기도 하다. 국내에서 가장 유명한 사례는 봉준호-송강호가 생각이 나는데, 그 외에도 홍상수-유준상(물론 지금은 김민희인 것 같지만 뮤즈가 한 명만 있으라는 법은 없지 않는가), 윤종빈-하정우도 생각난다. 인트로가 길었는데, 쿠엔틴 타란티노의 뮤즈는 누구일까? 우마 셔먼, 디카프리오, 사무엘 L 잭슨 등 여러 이름이 떠오르지만 개인적으로는 크리스토프 발츠를 뽑고 싶다. <장고>에서 닥터 킹 슐츠의 매력으로 크리스토프 발츠를 기억하는 사람들은 <바스터즈>의 한스 란다가 동일 인물임을 쉽게 떠올리기 어려울 것이다. 비슷하면서도 굉장히 결이 다른 두 연기를 보면 정말 저런 사람이 그 시대에 꼭 있었을 것만 같다는 생각을 하게 만든다. 특히, 바스터즈의 한스 란다를 연기할때는 오히려 연극같은 부분이 종종 느껴지는데, 이 역시 굉장히 큰 매력이니 안 본 사람들은 한 번 봤으면 좋겠다. 이 둘을 제외하고는 타란티노의 작품중에서 썩 마음에 드는 작품은 없는 것 같다. 다만, 본인의 은퇴작이라고 밝힌 10번째 영화가 아마 장고의 후속편이 될 것 같은데, 기대되는것 역시 사실이다. 

<장고>의 닥터 킹 슐츠(왼쪽). 치과의사가 본업이라서 외진용 마차에 어금니를 달고 다닌다. 

 

장고 세션은 끝나지 않았다. 그런데 이번 세션은 거의 CRUD를 적용한 것 같아서 같은 내용을 반복하면 재미가 없기 때문에 새롭게 알게된 점과 약간의 응용에 대해서 적어보려고 한다. 

 

 

∞ 디버깅

 

디버깅은 말 그대로 버그를 잡아주는 툴이다. vs code로 설정하는 방법은 다음과 같다. django를 설치한 상태에서 manage.py를 켜고 vs code왼쪽에 있는 무당벌레 모양 버튼을 누르면 launch.json이라는 하이퍼링크가 보인다. 누르고, django옵션을 클릭하면 된다. 그러면 아래와 같이 path를 지정한다는 조금 무서운 말이 나오는데, 겁먹지 말고 엔터치자. 

그리고 launch.json 파일에 들어가서 arguments에 --noreload--nothreading 이 두 항목을 추가해준다. 디버깅에서 중단점을 잡아줬을때 웹에서 자동으로 재시작이 되지 않게 하는 기능이라고 한다. 

 "args": ["runserver", "--noreload", "--nothreading"],

디버깅을 하면 "중단점"이라는 지점을 설정할 수 있다. 의미는, 코드를 실행할때, 중간에서 지금까지의 결과값을 도출해준다는 뜻이다. 예전에는 print를 사용해서 그 지점에서 어떤 값이 나오는지를 봤다면, 이제는 이렇게 확인할 수 있다. 중단점을 잡고 싶은 코드의 왼쪽에 줄 번호가 있는데 그 줄 번호의 왼쪽을 클릭하면 빨간 점이 생긴다. 또, 초록색 재생 버튼을 눌러 디버깅을 돌렸을때 에러를 조금 더 친절하게 알려주는 것 같다. 

 

 

∞ ForeignKey

 

ForeignKey는 모델과 모델간의 종속관계를 정리해준다. 블로그 글을 작성할 때 등에 사용되는데, 작성자가 게시글과 댓글을 달았을때 작성자가 댓글과 게시글을 소유하고 있고, 게시글은 댓글을 소유하고 있는 방식을 구현할 때 사용한다. 비유를 하자면 노비에게 찍혀있는 낙인이라고 볼 수 있다. 그래서 foreignkey는 댓글에 게시글과 작성자에 대한 낙인이 있고, 게시글은 작성자에 대한 낙인이 있다.  

class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    writer = models.ForeignKey(User, on_delete=models.CASCADE)


class Comment(models.Model):
    content = models.TextField()
    writer = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)

이 코드에서 보면 Post클래스에는 writer에게 ForeignKey를 주고, Comment클래스에는 writer와 post에게 ForeignKey를 주는 것을 볼 수 있다. 

 

ForeignKey는 one-to-many방식이라고 볼 수 있다. 주인이 여러 명의 노비를 가지고 있는 것과 비슷하다. 이 관계를 지정하는 함수를 필드, field라고 하는데 one-to-many 뿐만이 아니라, one-to-one나, many-to-many등의 방식을 구현할 수도 있다. one-to-one은 사용자와 계정의 관계, many-to-many는 게시글과 태그의 관계를 생각해볼 수 있다. 

 

 

∞ 장고 사용자 인증

 

위의 ForeignKey가 중요한 이유는 객체를 분류에 따라서 sorting하거나 사용자가 본인이 생성한 객체에 대해서만 update나 delete를 하기 위해서이다. 그러면, 본인이 생성한 객체인지 어떻게 알 수 있을까? 아래와 같은 코드를 사용한다. 

def post_delete(request, dpk):
    if request.method == "POST":
        post = Post.objects.get(id=dpk)
        if request.user == post.writer:
            post.delete()
            return redirect('post:Ulist')
        else:
            return HttpResponse('자기글만 삭제 가능')
    else:
        return HttpResponse(' 해당 url은 post요청만 받음')
        
def comment_create(request, pk):
    if request.method == "POST":
        if request.user.is_authenticated:
            post = Post.objects.get(pk=pk)
            new_comment = Comment()
            new_comment.writer = request.user
            new_comment.content = request.POST.get("Vcocontent")
            new_comment.post = post
            new_comment.save()
            return redirect('post:Udetail', pk=pk)
        else:
            return HttpResponse('로그인 필요!')
    else:
        return HttpResponse(' 해당 url은 post요청만 받음')

게시글을 삭제하는 함수와 댓글을 생성하는 함수를 예시로 가져왔다.

 

post delete의 경우에는 if-else문을 사용해서 GET메서드(url을 직접 입력하는 방식)은 아예 차단을 했고, POST메서드에 대해서도 request.user와 post.writer가 같으면 지우는 권한을 주고, 같지 않으면 HttpResponse를 호출하는 방법을 사용했다. request.user와 post.writer는 위에서 ForeignKey를 on_delete=models.CASCADE를 사용해서 부여했고, 이를 비교한다. 

 

comment create는 마찬가지로 GET메서드는 사전에 차단을 했다. 그리고 request.user.is_authenticated를 사용해서 접근한 사용자가 권한이 있는지, 여기서는 로그인을 했는지를 확인한다. 우리는 이전에 createsuperuser 계정을 만들었기 때문에 권한이 있는 것으로 표시된다. 아직 사용자 회원가입과 로그인을 만드는 방법을 배우지 않았다. 배우게 되면 이를 이용해서 구현하면 될 것 같다. 

 

 

∞ objects sorting

 

list의 형태로 반환되는 객체들을 이전까지는 생성순으로 정렬했다. 하지만 이제는 .order_by()함수를 사용해서 정렬되는 순서를 임의로 정할 수 있다. 아래는 view에서 list의 함수를 가져온 내용이다. id의 역순, 즉 생성의 역순으로 내용을 정렬할 수 있다. 이렇게 하면 가장 최근에 쓴 articles가 제일 위로 올 것이다. 

articles = Article.objects.all().order_by('-id')

추가로, 내가 원하는 정보만을 골라서 볼 수도 있다. .filter()함수를 이용했고, id가 1인 article을 가져온다. 여기에 articles.writer=1 등을 입력하면 id가 1인 사람이 작성한 모든 articles를 불러오는 것도 가능할 것이다. 

articles = Article.objects.all().filter(id=1)

 

 

∞ ctx = {'posts': post} 의 진실

 

나중에 for post in posts(posts는 리스트)의 형태를 가져와서 for문을 돌리기 위해서 딕셔너리의 형태로 키:밸류 값을 지정해준다. 그리고 나중에 해당하는 밸류값을 가져와서 for문을 돌 동안 html에 보여줌

 

 

∞ {% csrf_token %} 의 진실

 

이 코드는 일종의 암호문인데, 유저정보에 같이 딸려서 보낸다. 이 방법을 통해서 유저 정보를 확인할 수 있다. 그렇기 때문에 post방식에 필요한것 같다. 

그리고 강의자님도 안중요하다고 잘 모르겠다고 하시더라. ㅎㅎ

 

 

∞ request?

 

request의 형식을 별도로 지정해주지 않으면 모든 요청을 다 받을 수 있다. post, get등 모두 다 받을 수 있다. 

 

 

∞ 파이썬 터미널에서 객체 생성 방법

 

강의자님이 무릇 개발자라면 admin페이지에 들어가서 만드는 것 보다, 터미널창에서 모든 기능을 구현하게 하는 방법을 알아야 한다고 하셨다. 그런데, 이 기능을 사용하려면 기본으로 깔려있는 python으로는 안되고, ipython을 설치해야한다. 

pip install ipython

python manage.py shell

이 코드대로 ipython을 설치하고, shell모델을 불러온다. 그냥 ipython을 실행하면 오류가 뜬다. shell을 불러오면 터미널창에서 

초록색 in과 빨간색 out이 뜬다. out은 처음부터 보이지 않고, 초록색 in 에서 명령어를 입력했을 때 결과값으로 출력되는 줄이다.  다음 코드를 입력해서 객체를 생성하자.

from <appname>.models import <ClassName>

ClassName.objects.create(title='title', 'content'='content')

(title='title', content='content')는 임의로 클래스의 속성을 생각해서 작성한 내용이고, 실제로 객체를 만들때는 속성에 맞는 값을 지정해주면 된다. 

 

여기서 중요한 점이 있는데, objects를 호출할때 항상 뒤에 s를 붙여야 한다. 뭔가 하나만 호출할때는 object일 것 같은데 절대 아니다. 무조건 objects로 뒤에 s를 붙이자.

 

ClassName.objects.all()

이 코드를 실행하면 QuerySet의 형태로 리스트가 반환된다. QuerySet은 SQL을 생성해주는 인터페이스이다. 정확히 무슨 말인지는 나도 잘 모르겠다. 

 

 

∞ 검색창 만들기

 

우리가 위에서 살펴본 .filter()함수를 사용하면 검색창을 만들 수 있다. 

<h1>Ask Company</h1>
<form action="" method='GET'>
    <input type="text" name='q' value='{{q}}' />
    <input type="submit" value='search' />
</form>

<body>
    {% for item in item_list %}
    <div>
        <h3>{{ item.name }}</h3>
        <h4>가격: {{ item.price }}</h4>
        <p>{{ item.desc }}</p>
    </div>
    {% endfor %}
</body>

html파일의 코드이다.

 

from django.shortcuts import render
from django.http import HttpResponse
from .models import Item

# Create your views here.


def archives_year(request, year):
    return HttpResponse('{}년도에 대한 내용'.format(year))


def item_list(request):
    qs = Item.objects.all()
    q = request.GET.get('q', '')
    if q:
        qs = qs.filter(name__icontains=q)
    return render(request, 'shop/item_list.html', {
        'item_list': qs,
        'q': q,
    })

view.py함수의 코드이다. 

 

from django.contrib import admin
from .models import Item


@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):
    list_display = ['pk', 'name', 'short_desc', 'price', 'is_publish']
    list_display_links = ['name']
    search_fields = ['name']
    list_filter = ['is_publish', 'updated_at']

    def short_desc(self, item):
        return item.desc[:20]

admin.py함수의 코드이다. 특이한건, admin에 이 내용을 넣어준다는 것이다. 왜일까? 모르겠다. 지금까지 이런 느낌의 함수는 models.py에 들어갔었는데

 

어쨌든, 위 코드들을 사용하면 검색창을 만들 수 있다. 

당연히, 위의 검색창에 단어를 입력하면 제목에서 filtering해서 결과값만을 알려준다.

 

오른쪽에 있는 툴바는 debug-toolbar인데, 아직은 이에 대한 실효성을 잘 못느끼고 있다. 만약 알고보니 엄청난 도구였다면, 해당 내용에 대해서 나중에 또 포스팅 하겠다. 

 

 

∞ makemigrations, migrate 오류

 

출처: https://www.programmersought.com/article/25073071620/

에러를 직접 만들어서 캡처하려고 했는데, 또 만드려고 하니 에러가 안생긴다. 이 에러는 모델에서 새로운 속성을 만들고 makemigration을 하려고 할 때 나타나게 되는 오류이다. 새로운 속성을 만드려고 할 때, 이전에 생성된 객체들은 해당 속성이 없기 때문에 앞에 만든 애들은 어떻게 해줄까? 라는 의미이다. 1번과 2번을 누를 수 있는데, 1번은 현 상태에서 이전 객체들에게 값을 지정해주는 것이고, 2번은 일단 이 에러를 벗어난 뒤에, 내가 알아서 고치겠다는 의미이다. 간단한 해결방법은 1번을 입력한 뒤에 빈 문자열 ''을 입력해주면 오류 없이 끝난다. 물론 이전의 애들은 해당 속성에 대해 아무 값을 가지지 않는다. 

 

 

∞ render와 redirect의 차이?

 

간단하다. render는 다른 template를 지정할 때 사용하고, redirect는 다른 url로 이동할 때 사용한다.

render(request, template_name, context=None, content_type=None, status=None, using=None)

redirect(to, *args, permanent=False, **kwargs)

약간 쓰다보니 트러블슈팅 특별편같아 보여서 위에다가 troubleshooting이라고 제목을 고쳐야겠다. 갑자기 장고만 이렇게 길게 하니까 조금 적응이 안된다. 그리고 왜 장고는 똑같은 걸 해도 오류가 버라이어티하게 나는건지 모르겠다. 거의 옛날에 드론 만들면서 I2C 에러 나지 말라고 찬송가 부르면서 아두이노 연결하던 느낌이다. 사실 그렇게 해서 에러가 안난다면 무조건 하지

 

 

관련글 더보기

댓글 영역