[Python] unpacking_operator (=*) 와 그 사용법 (positional_argument, keyword_argument) Python

이 글은 python 에서 사용되는 일종의 자료형(?)에 속하는 
positional_argument 와  keyword_argument 에 대한 설명입니다. 

일단 각각의 개념정의와 용법을 알아봅시다.

1. unpacking operator (=*)

파이썬의 list 타입 변수와 dict 타입 변수를 함수 인자값으로 활용하기 위해 사용되는 연산자 입니다.
아래에서 예제까지 들어 확실하게 이해를 돕겟지만, 가령 아래와 같이 생각하시면 됩니다.
--------------------------------------
리스트의 경우

a = [1, 2, 3] 가 있다면,
*a 는 함수 인자값으로 1, 2, 3 이 됩니다.
괄호가 벗겨지게 되죠 ㅎㅎ
--------------------------------------
Dictionary 의 경우는 그럼 어떨까요?

d = {'a':1, 'b':2} 가 있다면
**d 의 경우 a=1, b=2 와 같이 변합니다.

자, 그럼 각각의 설명과 예제를 통해 하나씩 보시죠


2. Positional arguments (=args)

직역하면 위치(순서)를 가진 인자값들 이 됩니다.
즉, 순서를 가진 연속적인 복수개의 단일 입력값 을 지칭하고요.
함수 argument 들의 선언순서에 따라 입력값을 한번에 집어넣고자 할때 쓰입니다.

아래 예제를 한번 보시죠.


def mysum(a, b):
return a + b

tmp_list = [2, 6]
result = mysum(*tmp_list)
print(result) # 8

여기서 *tmp_list 에 주목하실 필요가 있습니다.
분명히 리스트에 들어있던 2, 6 값이 각각 mysum 함수의 a 와 b 에 각각 맵핑되었죠?
이렇게 함수 순차 인자값으로 리스트에 있던 element 들을 한방에 활용이 가능합니다.


3. Keyword arguments (=kwargs)

파이썬 함수는 argument 의 key를 직접 지정할 수 있습니다.
그래서 kwargs 같은 경우에는, 키워드별 인자값들 이라는 직역처럼,
Key 와 Value 지정이 가능한 함수 argument 의 input 을 한번에 집어넣고자 할때 쓰입니다.

아래 예제를 한번 보시죠.


def mycalc(a=1, b=2, c=3):
return a + b - c

mydic = {'a': 10, 'c': 8}
print(mycalc(**mydic)) # = 0

mydic2 = {'a': 4, 'b': 7, 'c': 6}
print(mycalc(**mydic2)) # = 5

인자값들을 mydic 이라는 dict() 형 변수에 다 담은뒤,
변수앞에 ** 을 붙여서 한방에 함수로 넘기는것을 보실 수 있습니다.



참고했던 링크입니다~ ㅎ 궁금하신분은 한번 보셔요!
https://stackoverflow.com/questions/7527849/how-to-extract-parameters-from-a-list-and-pass-them-to-a-function-call



[Django] ORM 으로 Multiple Field 대상 Group By 날리기 Python

Django 로 Group By 쿼리를 날리는 것에 대해서는
이미 한번 작성했던 포스팅 으로 정리한 바 있습니다.

그래서 이번에는 약간 업그레이드 된 버전의 Group By 쿼리를 장고 ORM 을 날리는 법을 찾아왔뜸.!!

이번 포스팅에서는

1. 2개 이상의 필드에 대한 Group By 
2. Date 필드에서 시간 OR 일 단위 Group By 
3. 1+2 를 같이!! 사용한 Group By

요렇게 3가지 Group By 를 알아볼 것입니다.

자, 그럼 하나씩 알아봅시다.
먼저, 예제에 사용할 Model 코드부터!!! 한번 봅시다.


from django.db import models
from django.db.models import functions


class Company(models.Model):
name = models.CharField("회사명")


class Worker(models.Model):
# id(PK) 필드는 생략됨
age = models.IntegerField("나이")
gender = models.CharField("성별", choices=(('male', '남'), ('female', '여'),))
join_dt = models.DateTimeField("입사일")
company = models.ForeignKey(Company, related_name="workers")


회사와 그에 딸린 직장인들 정보를 담은 모델이 있습니다.

그럼, 먼저. 첫번째 그룹핑을 한번 해보죠.

제가 하고싶은것은
최근 일주일 입사한 Worker들을 대상으로, 
소속회사 + 성별 단위로 그룹핑 카운트를 하고 싶습니다.
ORM 은 아래와 같습니다.


from datetime import datetime, timedelta

text = functions.Concat(
'company_id', models.Value('_'), 'gender',
output_field=models.CharField())

now = datetime.now() # 현재 2017-09-08 13:30:00
end_dt = now.replace(minute=0, second=0) + timedelta(days=1) # end_dt=2017-09-09 0:00:00
start_dt = end_dt - timedelta(days=7) # start_dt=2017-09-02 0:00:00

users = Worker.objects.filter(
join_dt__gte=start_dt,
join_dt__lt=end_dt
).annotate(grouping=text).values(
'grouping', 'company__name', 'gender'
).annotate(models.Count('id'))

print(users)

지난번 포스팅에서 설명 드렸다 시피 annotate() 가 사용된 query_set 의 value() 함수의 argument field 들이 group by 의 기준이 됩니다.
여기서는 그룹핑을 할 데이터를 company_id 와 gender 를 묶은 텍스트를 사용하여 두마리 토끼(=필드)를 다 잡았습니다.

------------------------------------------------------------------------------------------

그럼,  두번째 그룹핑...

이번에 하고 싶은것은 성별이 여자인 Worker 들을
입사 날짜의 "시간 단위"로 Group By 하여 나이의 합계를 알고 싶습니다.

뭐.. 쿼리가 참... -ㅅ- ;;; 이딴 데이터를 어따쓰나...싶긴 하지만
예제니까 한번 봅시다 ㅋㅋㅋ


users = Worker.objects.filter(gender='female')
.annotate(join_ymd_h=functions.Substr('join_dt', 1, 13))
.values('join_ymd_h').annotate(age_sum=models.Sum('age'))

print(users)

Substr() 함수가 쓰인것이 보이시죠?
index 가 1인 글자 부터 13개의 글자를 뜯어 group by 기준으로 삼습니다.
왜 이렇게 했냐고요? 보통 날짜값이 'YYYY-mm-dd HH:MM:SS' 요렇게 들어가죠?
이 문자열을 앞에서부터 13개를 뜯어내면 요렇게 됩니다.
'YYYY-mm-dd HH'
딱 시간값 까지 가져오죠? ㅎㅎㅎ

------------------------------------------------------------------------------------------

마지막으로 세번째 그룹핑. !!!
요건 앞에 첫번째, 두번째 그룹핑의 코드를 잘 보고 응용만 하시면 되는데요~

요번에 할 Group By 는 2017년 이후에 입사한 Worker 이면서,
입사날짜의 월별 + 소속회사 단위로 
그룹핑된 직원들의 카운트를 알고 싶습니다.

정답을 쪼곰 아래쪽에 둘테니,,,
보지 말고 한번 스스로 해보세요~
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

start_dt = datetime(year=2017)
workers = Worker.objects.filter(
join_dt__gte=start_dt
).annotate(join_ymd_h=functions.Substr('join_dt', 1, 13)).values(
'join_ymd_h', 'company_id'
).annotate(models.Count('id'))

print(workers)


자, 그럼 오늘 포스팅의 결론을 적고 마무리 하겠습니다.

1. values().annotate() 조합으로 django group by 를 할수 있다.

2. 이때, values() 의 argument 필드들은 group by 기준이 되며, 
복수개를 적으면 복수개 group by가 된다.

3. 복수개 필드의 group by 는 필드값을 concat으로 묶은 스트링을 사용할 수도 있다.

4. 날짜 필드의 시간값까지 추출하는 방법은 substr 함수를 사용하여 13번째 자리까지 글자를 추출하면 된다. 


누군가에게는 도움이 되길 바라며,,, 항상 즐코딩 하십시요 ㅋㅋ


+ 추가

이건 나중에 알게된 사실이라 그냥 덧붙임.
MySQL 의 쿼리 함수를 직접 사용하고 싶을경우 extra() 를 쓰면 된다.
그래서 날짜별 시간단위 group by 를 아래와 같이 할수도 있음.


logs = TAdvertisementRewardLog.objects.filter( # Asia/Seoul to UTC timezone
created_dt__gte=timezone.make_aware(start_dt),
created_dt__lt=timezone.make_aware(end_dt)) \
.extra({'ymd': "DATE(created_dt)", 'hour': "HOUR(created_dt)"}) \
.values('ymd', 'hour', 'advertisement_id') \
.annotate(conversion=models.Count('uid'))

그냥 생짜코드를 가져왔으므로.. 알아서 유추해서 보세요~ ㅎㅎㅎ




[Django] ORM 으로 Group By 쿼리 날리기. Python

Django ORM 에는 group by 를 명시적으로 날릴만한 함수가 준비되어있지 않다.
group by 를 위해 documentation 을 보다보면 그저 보다보면 아... 이게 뭐여.. 가 절로 나오기도 할것이다.

하지만 의외로 간단하게 할 수 있다.
아래 raw 쿼리와 Django ORM 코드를 같이 써두었으니 참고하도록 하자.



from django.db.models import Count, Model

class User(Model):
age = models.IntegerField()
gender = models.CharField()
company = models.CharField()
join_dt = models.DateTimeField()

# select age, count(id) as id__count
# where gender == 'male' group by age
users = User.objects\
.filter(gender='male')\
.values('age')\
.annotate(Count('id'))

# select age, company, count(id) as id__count
# where gender == 'male' group by company, age
users2 = User.objects\
.filter(gender='male')\
.values('company', 'age')\
.annotate(Count('id'))

# select age, count(id) as id__count , gender, company, join_dt
# where gender == 'male' group by age
users3 = User.objects\
.filter(gender='male')\
.values('age')\
.annotate(Count('id'))\
.values()



이 코드를 보면서 우리는 몇가지를 알 수 있다.

1. annotate 를 활용하여 aggregation 효과와 함께 group by 쿼리를 시전할 수 있다. 
   지금 예제에서는 Count 만을 활용하였는데, 저 함수 이외에 Sum, Avrg 등의 다른 aggregation 용 함수들을 활용할 수 있다.

2. annotate 하기 전 values() 에 입력되는 필드명이 group by 기준 필드가 된다.

3. 전체 필드를 선택 (= select *) 하고 싶다면 맨 뒤에 values() 만 간단히 붙여주면 되겠다.

잘 모르겠는 분들은 자신 장고의 버전에 맞는 Aggregation Documentation 을 참고하시길...




[Django] Resolve Error: get() returned more than one SomeModel -- it returned 6! 오류 해결하기 개발 삽질이야기

MultipleObjectsReturned at /some/url

get() returned more than one SomeModel -- it returned 6!

Django 개발을 하다가 위와같은 오류를 보았다면 필시,
Model 간의 Many to one relationship 을 확인해 보아야 한다.

Model A : Model B = 1 : N
의 관계를 가져야 한다고 했을때,

models.ForeignKey(...) 코드를 어떤 모델에 넣어야 할까?
정답은!
.
.
.
N 에 해당하는 Model B 이다.

위의 에러는 ForeignKey 선언을 1 에 해당하는 Model A 에 넣어놓은 상태에서 아래와 같은 코드를 사용했을때 나타난다.

<에러 재현>
class B(models.Model):    
# some codes ...
a_id = models.IntegerField()

class A(models.Model):
# some codes ...
b_objects = models.ForeignKey(B, db_column='id', to_field='a_id')

# A model 객체 1개만 불러온뒤, 그에 연결된 B 모델 객체들을 출력
a_obj = A.objects.first()
print(a_obj.b_objects) # <-- 불러온 A 모델 객체가 B 모델의 객체 6개를 가져올 경우, 여기서 에러가 남
<정상 코드>

class A(models.Model):
# some codes ...

class B(models.Model):
# some codes ...
a_obj = models.ForeignKey(A, db_column='a_id', related_name='b_objects')


# A model 객체 1개만 불러온뒤, 그에 연결된 B 모델 객체들을 출력
a_obj = A.objects.first()
print(a_obj.b_objects) # <-- B Model 에 있는 related_name 을 사용하여 객체들을 불러옴 에러 안남

<요약 & 결론>

One-to-many 관계를 Django ORM 으로 구현하고자 할때는,

왠만하면 many 에 해당하는 모델에 ForeignKey() 를 넣도록 하자.

related_name 속성을 잘만 이용한다면, 편하게 one 으로부터 many 모델객체를 불러올 수 있다.

여기서 주의할점은, related_name 을 사용한 파일에서 many 에 해당하는 모델을 import 하고 있어야 한다는것 ! ! !
만약에 이를 빠뜨리면 해당 필드가 없다는 오류가 뜰것이다.

[Django] ContentType Framework app_label 업데이트 전 알아두어야 할것 개발 삽질이야기

Django 에는...  content_type framework 라 하여,
모델을 정의할때 단일 객가 여러 다른 모델과 Generic 하게 Relation을 가질 수 있도록 하는 기능이 있다.
즉, 국경을 뛰어넘는 멀티 Relation !!! 이 가능한.. 뭐 그런게 있음.

그런데 이때, 그 relation을 걸수 있도록 해당 모델의 정보를 기록하는 테이블이
django_content_type

여기에 app_label 이라는 필드가 있는데, 이 값은 해당 모델의 코드가 위치한 django application 명을 의미하고.
model 이라는 필드는 테이블 명을 의미한다.

django 가 처음 구동 될 시, 이 django_content_type 테이블의 app_label 과 model 필드의 정보에 근거하여
모든 모델의 generic relation 그래프?를 초기화 한다.

한번 웹서버가 구동된 이후에는 django_content_type 값을 업데이트 하여도,

서비스에 반영되진 않는다.


그러므로 문제가 되는것이 만일 Generic Relation 에 사용되는 모델 코드의 위치의 application 변경이 이루어 진다면!!!
프로젝트의 리얼 배포를 앞두고 아래의 순서를 반드시 지켜야 한다.

1. django_content_type 에 날릴 write query 실행
2. 코드 배포

-----------------------

이글을 쓴 본인은
코드 배포 > django_content_type 데이터 update 
순으로 배포를 진행했다가 뒤늦게 깨닫고 화들짝 놀라 다시 uwsgi 를 restart 하였다.

그리고, 앞으로는 이 실수를 반복하지 않기위해 이렇게... 별것도 아닌것 같아 보이는
포스팅을... 쓴 것이다.
( 절대 별것이다 ! ! ! ! )

여러분은 처음부터 실수 안하시길!!!


1 2 3 4 5 6 7 8 9 10 다음


통계 위젯 (블랙)

10175
1045
129196

GoogleAdsenseResponsive

Cluster map