카테고리 없음

내일배움캠프 27일차

barryjung 2023. 4. 21. 22:33

[오늘 한일]

  • 알고리즘 문제풀이
  • 장고 심화강의 수강
  • 기초프로젝트 복습

 

[오늘 배운점]

 

<리스트 컴프리헨션은 빠른가>

오늘 알고리즘 문제를 풀다가, 한가지 의문점이 생겼다.

 

같은 동작을 두방법으로 작성할수 있었는데,

이터러블한 데이터를 리스트로 반환해야 했다.

 

 

list(range(0, 10))
[x for x in range(0, 10)]

예컨대 위와 같다.

이터러블 값을 리스트에 담는 방법은 list연산, 리스트 컴프리헨션이 있다.

 

※ map연산이나 lambda식을 조합하여 풀수 있었는지도 모르겠다.

map, lambda는 잘 안쓰다보니 익숙하지 않다.

자주 사용하며 익혀야 할 것 같다.

 

 

그런데 동작 상 두 방법 모두 적합해서 하나를 고를 필요가 있었다.

페어 프로그래밍을 한 팀원분은 위 방법이 시간 복잡도가 우위일거 같다고 하셨다.

 

생각해보니 그럴것 같았다.

위 코드는 이터러블 데이터를 리스트로 만드는 연산이고,

아래 코드는 같은 데이터를 요소로 반복하여 리스트를 작성할 것처럼 보인다.

 

 

하지만 나는 리스트 컴프리헨션 또한,

list 연산 못지 않게 간결할것이라고 생각되었다.

 

사실 자세한 동작까지는 모르겠다.

기회가 된다면, 튜터님들께 질문 드려봐야겠다.

다만 나는 두 동작의 동작시간을 비교해보고 싶었다.

인터넷을 검색해서 한방법을 알수 있었다.

 

from timeit import timeit

t1 = timeit('print(list(range(0, 10)))', number=10)
print(t1)	#0.0015802999999999998

t2 = timeit('print([x for x in range(0, 10)])', number=10)
print(t2)	#0.0016587000000000025

timeit모듈을 이용해 시간 비교를 작성해본 내용이다.

결과는 거의 비슷하다.

여러 차례 실행해보면 시간차가 더 좁혀지기도 한다.

 

결론, 친숙한 리스트 컴프리헨션의 손을 들어주고 싶다.

반대로 친숙하지 않은 연산이나 map,lambda식도 골고루 공부해야겠다는 생각이다.

(컴프리헨션을 쓰다보니 map,lambda는 잘 안쓰게 된다.

lambda는 조건식으로 조합하기도 하던데 잘 알아둬야 할것 같다.)

 

자세한 내용은 나중에 질문드려봐야겠다.

 


<장고 admin페이지 커스터마이즈 시 유의점>

오늘도 장고 프로젝트를 복습했다.

어제 오늘 진행해 90%정도 따라 만들었다.

 

우리팀은 admin페이지를 커스터마이즈 했었는데,

그중 list_display에 대해 유의점이 있다.

 

class UserModelAdmin(admin.ModelAdmin):
    list_display = ['id', 'username', 'image', 'short_bio']	#OK
    
    list_display = "__all__"	#Error

list_display는 위에 작성처럼 리스트 형태로 선언해야만 한다. 

아래처럼 "__all__"로 작성해도 될까 싶었는데 안된다.

 

<class 'tweet.admin.CommentModelAdmin'>: (admin.E107) The value of 'list_display' must be a list or tuple.

이런 에러메세지가 나온다.

 

※모델에 __str__메소드를 줘야지,

외래키 필드등에서 인스턴스명이 아닌 특정 데이터가 보인다.


<login_required 없이 로그인과 사용자 검사>

장고의 view함수에는 request가 불러와진다.

request.user는 요청한 사용자를 가르키는데,

이를 적절히 이용하면 간결하게 코드를 작성할수 있다.

 

우선 유저와 관련한 CRUD기능에서는,

현재 사용자를 다 request.user로 활용하면 된다.

모델에서 오브젝트를 호출 안해도 되니 간결하다.

 

def followpage_view(request):
    follow_users = request.user.follow.all()
    follow_tweets = TweetModel.objects.filter(
        user__in=follow_users).order_by("-created_at")
    return render(request, 'tweet/followpage.html', {"tweets": follow_tweets})

예를 들어 팔로우 페이지는 이렇게 작성할수 있다.

 

또 한가지 장점.

팔로우 페이지 등 로그인 사용자에게만 제공될 기능은,

url에 id를 입력 받지 않아도 된다.

 

현 사용자에 대한 구분자가 필요 없기 때문이다. (이미 알고있기 때문에)

url에 id가 없으니, 해당 페이지는 무조건,

로그인한 사용자 자신에 대한 정보만 나오게 동작하게 된다.

 

그리고 url 조작을 통한 타인의 정보 접근 시도를 차단 할수있다.

힘들게 함수 내에서 검사하지 않아도 되는 것이다.

 

또한 로그인 하지 않은 상태에서 해당 페이지에 접근하면,

현재 사용자가 없기에 페이지가 작성되지 않는다.

로그인에 대한 체크도 해결이다.

 

로그인 하지 않은 상태에서 팔로우 페이지에 접근했다. 에러 메세지의 모습.

이런 에러가 나온다.

 

이것을 그냥 비정상 접근에 대한 응답이라고 삼는 다면,

아주 간결하게 로그인과 사용자에 대한 검사를 할수 있는 것이다.

 


<쿼리셋을 이용한 역참조 방법이 있을까>

def followpage_view(request):
    follow_users = request.user.follow.all()
    follow_tweets = TweetModel.objects.filter(
        user__in=follow_users).order_by("-created_at")
    return render(request, 'tweet/followpage.html', {"tweets": follow_tweets})

위에서 적었었던 팔로우 페이지 코드 내용이다.

 

나는 처음에 더더욱 간결하게 작성하려 했었다.

request.user.follow.tweetmodel.all() 이렇게 이다.

 

request.user는 현재 사용자의 인스턴스이고,

그 인스턴스의 follow라는 속성은 user가 가진 manyTomanyField이다.

그럼 모든 팔로워가 호출될거고,

TweetModel의 역참조명인 tweetmodel을 작성한다음,

all()로 다 불러온다! 라는 식으로 작성해봤다.

 

생각과 작성만으로는 간결하고 좋았으나,

에러가 나며 실행되지 않았다.

 

 

동작되지 않은 이유는 금방 파악할수 있었다.

에러 페이지의 모습이다.

request.user.follow.tweetmodel.all()  이렇게 한방에 작성할 경우 왼쪽의 에러가 난다.

request.user.follow과 tweetmodel.all()을 구분동작으로 하면 오른쪽의 에러가 난다.

 

즉, request.user.follow라는건 매니투매니 필드이고 반환되는 값은 다수일것으로 기대된다.

그 동작 결과로 QuerySet이 반환되고, 이 QuerySet을 이용해서는 역참조 이름을 이용한 호출이 안되는 것이다.

 

그도 그럴것이, 다수의 데이터인 QuerySet을 이용해 또 다수의 데이터를 부르니까,

뭔가 방법이 다를것이라고 생각은 했다.

 

 

프로젝트에서 썼었던 해결방법이 위에 작성한것과 같다.

TweetModel에서 직접 오브젝트를 불러오며 filter에 저런 조건을 작성해서 값을 가져온다.

__in에 대해서는 나중에 자세히 알아봐야 겠다.

 

결론, 내가 해보려고 한 방법은 되지 않았다.

queryset을 역참조할수 있는 방법은 없는 것일지에 대해 아쉬움이 남는다.

이것도 방법을 알아봐야겠다.