파이썬에서 문자열을 지정하는데에는 큰 따옴표(")와 작은 따옴표(')가 있습니다. 두 가지가 존재하지만 어떤 것을 사용하던 전부 똑같습니다.

single = 'abcde가'
double = "abcde가"


assert single == double
print("single == double")


assert single is double
print (single is double)


# 결과
# single == double
# True


규칙은 아니지만 큰 따옴표와 작은 따옴표를 섞어쓰면 혼란을 야기할 수도 있으므로 아래와 같이 규칙을 정해 사용을 권합니다.

큰 따옴표 " (Double Quote)

  • 텍스트

작은 따옴표 ' (Single Quote)

  • 기호, 식별자

3개의 큰 따옴표 """

  • docstrings, 정규표현식

기타

파이썬 내부에서(3.4) 기본적으로 작은 따옴표를 기본으로 합니다. 하지만 아래와 같이 문자열에 작은 따옴표 기호가 존재할 경우 큰 따옴표로 묶입니다.

def test(a):
    print(a)
    
a = ['aaa', 'bbb', "ccc", 'ddd\'']
test(a)


# 결과
# ['aaa', 'bbb', 'ccc', "ddd'"]

작은 따옴표로 문자열을 묶었다 하더라도 파이썬 내부에서 자동으로 큰 따옴표로 묶어서 처리합니다. PostgreSQL과 같은 데이터베이스는 문자열을 insert할 때 큰 따옴표를 지원하지 않고 작은 따옴표만 지원합니다. 이러한 점을 유의하여 개발해야 합니다. 해결 방법 중 하나는 리스트 내의 작은 따옴표를 특수한 문자로 변경한 다음 (예를 들어 ` 같은 기호), string 타입으로 변경된 다음 다시 변환하도록 해야 합니다.

ssl error decryption failed or bad record mac 에러... 이전에 uWSGI에서도 문제가 발생한 적이 있었습니다. 이번에는 PostgreSQL 파이썬 모듈인 psycopg2에서 발생했습니다. 원인을 찾아보니 이러한 에러가 발생한 것은 PostgreSQL쪽이 아니라 ProcessPoolExecutor (병렬처리) 부분이였습니다.

 원인

DB connection과 cursor를 전역변수로 선언한 다음, 병렬처리를 실행하려 할 때 아래와 같이 함수에서 공통적으로 cursor, conn을 사용하려 하면 위와 같은 에러가 발생합니다.

from concurrent.futures import ProcessPoolExecutor
 
 
def concurrent(list):
	for i in list:
		query = 'insert into test (id) values ({0})'.format(list[i])
		cursor.execute(query)
	conn.commit()
 
db = Database() 
cursor, conn = db.connect() # 입력된 정보를 바탕으로 db 연결을 하여 cursor, connection을 반환해주는 함수가 있다고 가정
_list = ['1','11','111','1111','11111','111111']
 
 
pool = ProcessPoolExecutor(max_workers=4)
pool.map(concurrent, _list)


위 코드를 실행하는 순간 ssl error decryption failed or bad record mac를 발생하게 됩니다. 이유는 여러 프로세스가 동일한 데이터베이스의 공유하게되기 때문입니다.

해결책

해결방법은 다소 간단합니다.

아래와 같이 여러 프로세스가 동일한 데이터베이스를 공유하지 않도록 추가적으로 생성해주면 끝입니다. 

from concurrent.futures import ProcessPoolExecutor

def concurrent(list):
	cursor, conn = db.connect() # 각 프로세스마다 다른 데이터베이스 객체 생성
	for i in list:
		query = 'insert into test (id) values ({0})'.format(list[i])
		cursor.execute(query)
	conn.commit()
 
db = Database() 

_list = ['1','11','111','1111','11111','111111']

pool = ProcessPoolExecutor(max_workers=4)
pool.map(concurrent, _list)


위와 같이 cursor와 connection을 multiProcessing을 실행할 함수 내에 생성해주면 됩니다.

  1. JW 2018.07.05 17:22

    저도 비슷한 문제를 겪고 있습니다. 만약 Django 에서는 ORM을 쓰는데 `db.connect()` 부분은 어떻게 처리해야될까요?ㅠ direct하게 ORM을 쓰게되면 계속 에러가 발생하네요...

Mongo DB 용어

SQL 용어MongoDB 용어
일반 용어
데이터베이스(database)데이터베이스(database)
테이블(table)콜렉션(collection)
행(row)문서(document) / BSON 문서(BSON document)
컬럼(column)필드(field)
인덱스(index)인덱스(index)
테이블 조인(table join)임베디드 문서 및 링킹(embedded documents and linking)
주키(primary key)주키(primary key)
특정 컬럼을 주키로 지정주키가 _idfield로 자동 설정됨
집계(aggregation, 예: group by)집계 파이프라인(aggregation pipeline)

Python 내 Mongo DB 모듈 설치

$pip3 install pymongo

Connection과 Database, Collection 생성 및 선택

import pymongo
 

conn = pymongo.MongoClient('서버ip', port)
 
db = connection.AAA # AAA라는 이름의 데이터베이스 생성
collection  = db.test # test라는 이름의 테이블 생성
db = conn.get_database('데이터베이스명') # 데이터베이스 선택
collection = db.get_collection('컬렉션명') # 테이블 선택
 
## 예시
 
conn = pymongo.MongoClient('127.0.0.1', 27017)  # mongoDB에서 port를 변경하지 않았으면 기본값인 27017
db = conn.get_database('mongo_test') # mongo_test 데이터베이스 선택
collection = db.get_collection('test_table') # test_table 테이블 선택

Collection List

collection_list = db.collection_names() # 선택된 데이터베이스의 collection 목록들을 출력. return type = list
print (collection_list)

Insert

collection.insert({"number":0}) # 선택된 컬렉션에 키가 number, 값이 0인 데이터 저장

find (=select)

# 전체 조회
results = collection.find() # find()에 인자가 없으면 해당 컬렉션의 전체 데이터 조회. return type = cursor

for result in results:
  print (result)
 
# 조건 조회
results = collection.find({"id": {"$gt":90}}) # id가 90보다 큰 데이터 조회. $gt는 '~보다 크다'의 의미


for result in results:
  print (result)

Update

collection.update({업데이트를 위해 선택할 key-value쌍}, {수정될 내용의 key-value쌍}, upsert, 멀티라인 조건여부) # upsert와 멀티라인 조건여부의 default는 false

 
# upsert: True일 경우, 선택할 key-value가 존재하면 업데이트를 진행하고 존재하지 않으면 insert를 징행
# 멀티라인 조건여부: True일 경우, 선택할 key-value가 여러개가 존재할 때 전부다 수정될 내용의 key-value쌍으로 수정
 
## 예시
collection.update({'id':'5'}, {'id':'5','name':'kim'}, upsert=True, False) # id가 5인 데이터가 존재하면 {'id':'5','name':'kim'}로 update를 하고 존재하지 않으면 insert
collection.update({'id':'5'}, {'id':'5','name':'kim'}, upsert=True, multi=True)# id가 5인 데이터가 여러 개 존재하면 전부 다 {'id':'5','name':'kim'}로 수정

Remove (=delete)

collection.remove({"id": {"$gt":90}}) # id가 90보다 큰 데이터 삭제


queryset to dict!

QuerySet은 대개 Django ORM의 filter()함수를 사용할 때 (1개 이상의 데이터가 반환될 때) 갖게 되는 타입입니다.

이러한 타입을 그대로 사용하려고 하면 아래와 같이 출력이됩니다.

a = ModelObj.objects.filter(col=1)
 
# 결과
{col:[1], .... ,}

위와 같이 value값에 리스트가 씌워져 있는 것들을 확인할 수 있습니다.


QuerySet의 변환은 아래처럼 간단하게 dict로 변환이 됩니다.

a = ModelObj.objects.filter(col=1)

print(a.values())
# 결과
{col:1, .... ,}


django model object to dict!

모델객체에서 dict타입으로의 변환은 여러 방법이 있습니다. 참고로 Django ORM에서 model 객체로 결과가 나오는 것은 get()과 같은 형식이여야 합니다. filter()처럼 QuerySet으로 나오는 형태는 에러가 날 수 있습니다.

test case

모델

from django.db import models

class OtherModel(models.Model): pass

class SomeModel(models.Model):
    value = models.IntegerField()
    value2 = models.IntegerField(editable=False)
    created = models.DateTimeField(auto_now_add=True)
    reference1 = models.ForeignKey(OtherModel, related_name="ref1")
    reference2 = models.ManyToManyField(OtherModel, related_name="ref2")

데이터

other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.value = 1
instance.value2 = 2
instance.reference1 = other_model
instance.save()
instance.reference2.add(other_model)
instance.save()

출력되길 원하는 포맷

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 2}


위와같은 형태로 모델과 데이터가 존재한다 했을 때, instance 객체를 dict타입으로 변환하는 방법을 아래에서 설명합니다.

instance.__dict__

instance.__dict__
 
# 결과
{'_reference1_cache': <OtherModel: OtherModel object>,
 '_state': <django.db.models.base.ModelState at 0x1f63310>,
 'created': datetime.datetime(2014, 2, 21, 4, 38, 51, 844795, tzinfo=<UTC>),
 'id': 1L,
 'reference1_id': 1L,
 'value': 1,
 'value2': 2}

이 방법은 간단하지만 사용자가 원하는 key값으로만 나오는 것이 아니며 데이터가 추가되어서 나오기도 합니다. 위를 보면 reference2의 key가 빠졌습니다. 또한 reference1은 네이밍이 잘못 되었으며 두 개의 데이터가 추가되었습니다.

model_to_dict

from django.forms.models import model_to_dict
model_to_dict(instance)
 
# 결과

{u'id': 1L, 'reference1': 1L, 'reference2': [1L], 'value': 1}

위 방법은 대개 모델에 사용자가 직접 입력한 데이터만 출력하는데에 사용하면 적합합니다. 하지만 예제 모델에는 autoField가 존재하여 자동생성된 필드의 값은 출력되지 않은 것을 확인 할 수 있습니다.

model_to_dict with fields

from django.forms.models import model_to_dict
model_to_dict(instance, fields=[field.name for field in instance._meta.fields])
 
# 결과
{u'id': 1L, 'reference1': 1L, 'value': 1}

이 방법은 위의 model_to_dict의 표준방법보다도 못한 방법입니다.

query_set.values()

SomeModel.objects.filter(id=instance.id).values()[0]

# 결과
{'created': datetime.datetime(2014, 2, 21, 4, 38, 51, tzinfo=<UTC>),
 u'id': 1L,
 'reference1_id': 1L,
 'value': 1L,
 'value2': 2L}

이것은 instance.__dict__와 같은 결과이지만 추가적인 필드는 존재하지 않습니다.

Custom Function

장고의 model_to_dict 모듈에 대부분 답을 가지고 있습니다. 명시적으로 편집할 수 없는 필드들은 제거가 됩니다. 따라서  해당 검사(편집할 수 없는 필드를 삭제하는 부분)를 제거하면 원하는대로 작동하는 다음 코드가 생성됩니다.

from django.db.models.fields.related import ManyToManyField

def to_dict(instance):
    opts = instance._meta
    data = {}
    for f in opts.concrete_fields + opts.many_to_many:
        if isinstance(f, ManyToManyField):
            if instance.pk is None:
                data[f.name] = []
            else:
                data[f.name] = list(f.value_from_object(instance).values_list('pk', flat=True))
        else:
            data[f.name] = f.value_from_object(instance)
    return data


위 작업은 복잡하지만 위의 함수 to_dict(instance)를 호출하면 원하는 답을 받을 수 있습니다.

{'created': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
 u'id': 1,
 'reference1': 1,
 'reference2': [1],
 'value': 1,
 'value2': 2}


dict()함수를 사용하여 초기부터 dictionary타입의 데이터를 생성하던지 다른 타입의 데이터를 dictionary로 변환하는 방법에 대해 설명합니다.

dict()

a = dict()
a['id'] = 'test'
a['password'] = 'test'
print(type(a), a)
 
# 또는
 
temp = {'id':'test', 'password':'qwer'}
a = {}
for k, v in temp.items():
    print(k,v)
    a[k] = v
print(a)

dict(key,value)

a = dict({'id':'test', 'password':'qwer'})
print(type(a), a)

dict(**kwargs)

a = dict(id='test',password='qwer')
print(a)


각 다양한 방법이 존재하지만 key, value를 string 타입으로 몇 개를 받을지 모르는 상황이면 첫 번째의 방법보단 세 번째 방법이 더 좋아보입니다. 

결론: 상황에 맞게 사용

+ Random Posts