페이지

2016년 6월 22일 수요일

python django datetime timezone naive aware 정리

Python 3.5.1
Django 1.9.5
>>> from datetime import datetime
>>> # 현재시간을 확인해보겠습니다.
>>> now_naive = datetime.now()
>>> str(now_naive)
'2016-06-22 15:59:26.918742'
>>> # UTC 시간을 확인해보겠습니다.
>>> utcnow_naive = datetime.utcnow()
>>> str(utcnow_naive)
'2016-06-22 06:59:53.294294'
>>> # 두문자열을 보면 어떤게 어느지역 시간인지 분간할 수 없습니다.(naive datetime)

>>> # django 에서 제공하는 현재시간입니다.
>>> from django.utils import timezone
>>> timezone_now = timezone.now()
>>> str(timezone_now)
'2016-06-22 07:05:22.279485+00:00'
>>> # 위 문자열들과 비교해보면 뒤에 +00:00 이 붙어서 UTC 시간임을 알수 있습니다.(aware datetime)

>>> # naive 시간들을 aware 로 변경해보겠습니다.
>>> now_aware_utc = timezone.make_aware(now_naive,timezone.utc)
>>> now_aware_localtime = timezone.make_aware(now_naive,timezone.get_current_timezone())
>>> str(now_aware_utc)
'2016-06-22 15:59:26.918742+00:00'
>>> str(now_aware_localtime)
'2016-06-22 15:59:26.918742+09:00'
>>> # 앞의 시간값은 같은데 뒤의 timezone 정보만 다릅니다. utc로 지정하면 +00:00 이 붙고, current_timezone 으로 지정하면 +09:00 이 붙습니다. 즉 timezone 정보가 추가됩니다.
>>> # 따라서 naive 상태의 시간이 utc인지 current_timezone인지 명확히 알고 변경해야 합니다.
>>> # datetime.now() 는 current_timezone 이므로 now_aware_localtime 이 정확한 변환값입니다.

>>> # datetime.utcnow() 는 UTC 이므로 UTC를 지정하여 변환해줘야합니다.
>>> utcnow_aware_utc = timezone.make_aware(utcnow_naive,timezone.utc)
>>> str(utcnow_aware_utc)
'2016-06-22 06:59:53.294294+00:00'


>>> # 어떤것들은 +00:00 이 붙어서 출력되므로 일괄적으로 +09:00 이 붙은상태의 localtime으로 출력해보겠습니다.
>>> str(timezone.localtime(utcnow_aware_utc))
'2016-06-22 15:59:53.294294+09:00'
>>> str(timezone.localtime(now_aware_localtime))
'2016-06-22 15:59:26.918742+09:00'
>>> str(timezone.localtime(timezone_now))
'2016-06-22 16:05:22.279485+09:00'

참고문서

https://docs.djangoproject.com/en/1.9/topics/i18n/timezones/

2016년 6월 8일 수요일

django DEBUG = False ALLOWED_HOSTS 설정

django 개발시 기본 설정은 다음과 같습니다.
DEBUG = True
ALLOWED_HOSTS = []
DEBUG = False
로 변경하면 아래와 같은 에러가 발생합니다.
CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.

DEBUG = True 일때는 상관없지만 DEBUG = False 가 되면 접속가능한 호스트를
ALLOWED_HOSTS 에 추가해줘야 합니다.

ALLOWED_HOSTS = ['127.0.0.1'] 로 설정 후
http://localhost:8000/ 로 접속시
Bad Request (400)

http://127.0.0.1:8000/ 로 접속시
접속됨

ALLOWED_HOSTS = ['localhost'] 로 설정 후
http://localhost:8000/ 로 접속시
접속됨

ALLOWED_HOSTS = ['*']
로 하여 모든 곳으로 부터의 접속을 허용할 수도 있습니다.

DEBUG = False 로 하는 경우는 대부분 운영을 위한 경우이고
운영환경의 경우 아파치나 nginx 같은 웹서버를 경유하게 되므로
ALLOWED_HOSTS 에 서버의IP 만 적어 주는 것이 좋을것 같습니다.
그리고 웹서버에서 어떻게 요청을 전달하느냐에 따라 맞춰서 설정해 줘야 합니다.

django 에서 mysql 사용하기

데이터베이스 이름을 db1
유저명을 user1
유저암호를 user1_pw

데이터베이스 추가 및 유저 추가

create database db1;
grant all privileges on db1.* to 'user1'@'127.0.0.1' identified by 'user1_pw';

127.0.0.1 는 데이터베이스 서버와 django 서버가 다른서버에 있다면 django 서버의 IP 를 입력합니다.

mysqlclient 설치 - https://github.com/PyMySQL/mysqlclient-python

$ sudo apt-get install libmysqlclient-dev
$ pip install mysqlclient

데이터베이스 설정 변경 - project/settings.py

DATABASES 항목을 찾아서 다음과 같이 수정합니다.
DATABASES = {
    'default':{
        'ENGINE':'django.db.backends.mysql',
        'NAME':'db1',
        'USER':'user1',
        'PASSWORD':'user1_pw',
        'HOST':'mysqlhost.example.com',
        'PORT':'3306',
    }
}

데이터베이스에 적용 및 수퍼유저 생성

$ python manage.py migrate
$ python manage.py createsuperuser

django 에서 유저추가 방법

1. 수퍼유저를 만듭니다.

$ python manage.py createsuperuser

2. 각각의 유저를 만듭니다.

$ python manage.py shell
>>> from django.contrib.auth.models import User
>>> u = User(username='user1')
>>> u.set_password('1111')
>>> u.is_staff=True # admin 유저의 경우 True
>>> u.save()

참고로 django rest framework 에서
다음과 같은 IsAdminUser 권한을 설정하면
u.is_staff=True 로 되어있는 유저만 접속할 수 있습니다.

from rest_framework import viewsets
from rest_framework import permissions
class UserViewSet(viewsets.ModelViewSet):
  permission_classes = (permissions.IsAdminUser,)

2016년 6월 4일 토요일

Django Rest Framework 파일만 별도로 업로드 하는 기능 구현

Django Rest Framework Image Upload Download
에서 이어집니다.

functional_tests/tests_upload.py

  • 우선 기존테스트를 복사해서 test_upload_file 로 테스트를 하나 만듭니다.
  • title만 먼저 등록하고
  • /imageuploads/{pk}/upload 로 파일만 전송하도록 수정합니다.
  • 업로드한 파일을 지우고
  • 파일없이 요청하면 400 코드를 반환하게 합니다.

  def test_upload_file(self):
    # title 만 입력하여 추가한다.
    r = requests.post(self.live_server_url + '/imageuploads/',
        data={
            'title':'Test Image'
        }
    )
    self.assertEqual(201, r.status_code)  # created
    url = r.json()['url']
    # /imageuploads/{pk}/upload 로 파일을 전송한다.
    file = open('functional_tests/test_image.png','rb')
    files = [
        ('imagefile', ('test_image.png', file, 'image/png'))
    ]
    r = requests.post(url + 'upload/',
        files=files
    )
    file.close()
    self.assertEqual(200, r.status_code)
    self.assertEqual('upload success', r.json()['status'])
    # 업로드된 파일을 지운다.
    r = requests.get(url)
    imagefile = r.json()['imagefile']
    imagefilepath = imagefile.__str__().replace(url,'')
    imagefile_realpath = os.path.abspath(os.path.join(MEDIA_ROOT, imagefilepath))
    os.remove(imagefile_realpath)
    # 파일없이 요청하면 400 코드를 반환한다.
    r = requests.post(url + 'upload/')
    self.assertEqual(400, r.status_code)
    self.assertEqual('no file', r.json()['status'])

views.py

  • request.data 에서 첫번째 키 값을 찾고
  • 키 값이 있으면 저장해주고
  • 없으면 400 코드를 반환합니다.

from rest_framework.response import Response
from rest_framework import status
  @detail_route(methods=['post'])
  def upload(self, request, pk=None):
    key = None
    for k in request.data:
      key = k
      break
    if key:
      r = self.get_object()
      r.imagefile = request.data[key]
      r.save()
      return Response({'status': 'upload success'})
    else:
      return Response({'status': 'no file'}, status=status.HTTP_400_BAD_REQUEST)
전체 소스코드는 아래 링크를 확인하세요.
https://github.com/kyuhyung-park/djangorestframework_practice/tree/only_file_upload

2016년 6월 3일 금요일

Django Rest Framework Image Upload Download


Django Rest Framework Simple Start
에서 이어집니다.

업로드 테스트

command
(virtualenv) tutorial>pip install requests


functional_tests/test_image.png 준비

functional_tests/tests_upload.py
from django.test import LiveServerTestCase
import requests
class UploadTest(LiveServerTestCase):
    def test_upload(self):
        file = open('functional_tests/test_image.png','rb')
        files = [
            ('imagefile', ('test_image.png', file, 'image/png'))
        ]
        r = requests.post(self.live_server_url + '/imageuploads/',
            data={
                'title':'Test Image'
            },
            files=files
        )
        file.close()
        self.assertEqual(201, r.status_code)  # created

command
(virtualenv) tutorial>python manage.py test functional_tests

테스트 실행하면 업로드된 파일이 tutorial 폴더에 저장된것을 확인할 수 있습니다.

git tag upload_test

업로드시 파일 저장위치 변경

settings.py 에 다음을 추가합니다.
MEDIA_ROOT = os.path.abspath(os.path.join(BASE_DIR, '../uploadfiles'))

업로드 되는 파일이 프로젝트 루트 기준으로 /uploadfiles 가 됩니다.

models.py 에서 imagefile 에 upload_to 를 추가합니다. 
imagefile = models.FileField(upload_to='imagefile/%Y/%m/%d', null=True)

이 필드로 인해 업로드되는 파일은 /uploadfiles/imagefile/2016/06/02 형태가 됩니다.

functional_tests/tests_upload.py
from tutorial.settings import MEDIA_ROOT
import os
class UploadTest(LiveServerTestCase):
    def test_upload(self):
        # 기존 코드 이후에 추가
        imagefile = r.json()['imagefile']
        imagefilepath = imagefile.__str__().replace(self.live_server_url + '/imageuploads/','')
        imagefile_realpath = os.path.abspath(os.path.join(MEDIA_ROOT, imagefilepath))
        os.remove(imagefile_realpath)

업로드된 파일을 지우는 코드입니다.
파일이 없을 경우 os.remove(imagefile_realpath) 에서 에러가 발생합니다.

command
(virtualenv) tutorial>python manage.py test functional_tests
테스트를 돌려 확인해 봅니다.

git tag change_upload_path

다운로드 구현

serializers.py
class ImageUploadSerializer(serializers.HyperlinkedModelSerializer):
    imagefile_url = serializers.HyperlinkedIdentityField(view_name='imageupload-imagefile', read_only=True)
   
    class Meta:
        model = ImageUpload
        fields = ('url', 'pk', 'title', 'imagefile', 'imagefile_url')
views.py
from rest_framework.decorators import detail_route
from django.http import FileResponse
class ImageUploadViewSet(viewsets.ModelViewSet):
    queryset = ImageUpload.objects.all()
    serializer_class = ImageUploadSerializer
   
    @detail_route(methods=['get'])
    def imagefile(self, request, pk=None):
        r = self.get_object()
        # 확장자 추출
        ext = '*'
        if r.imagefile.path:
            ext = r.imagefile.path.split('.')[-1]
        content_type = 'image/' + ext
        # 다운로드용 Response 반환
        response = FileResponse(open(r.imagefile.path, 'rb'), content_type=content_type)
        return response
tests_upload.py
test_upload 메소드 명을 test_upload_download 로 바꿈.
아래 내용 추가
import shutil
import filecmp
        # 파일의 마지막 부분을 아래와 같이 수정
        # --------------------------------
        # 다운로드 경로를 알아내고 요청을 보낸다.
        imagefile_url = r.json()['imagefile_url']
        r = requests.get(imagefile_url, stream=True)
        # 다운로드한 파일을 저장한다.
        with open('functional_tests/download.png', 'wb') as out_file:
            shutil.copyfileobj(r.raw, out_file)
        # 업로드 한 파일과 다운로드 한 파일이 같은지 비교한다.
        self.assertTrue(filecmp.cmp('functional_tests/test_image.png', 'functional_tests/download.png'))
        # 다운로드한 파일을 삭제한다.
        os.remove('functional_tests/download.png')
        # --------------------------------
       
        # 업로드된 파일을 지운다.
        imagefilepath = imagefile.__str__().replace(self.live_server_url + '/imageuploads/','')
        imagefile_realpath = os.path.abspath(os.path.join(MEDIA_ROOT, imagefilepath))
        os.remove(imagefile_realpath)

git tag download

전체 소스코드는 아래 링크를 확인하세요.
https://github.com/kyuhyung-park/djangorestframework_practice/tree/download


Django Rest Framework Simple Start

Django Project Start


>virtualenv virtualenv --python=f:\Anaconda3\python.exe
>virtualenv\Scripts\activate.bat
(virtualenv) >pip install django
(virtualenv) >pip install djangorestframework
(virtualenv) >django-admin.py startproject tutorial
(virtualenv) >cd tutorial
(virtualenv) tutorial>django-admin.py startapp quickstart
(virtualenv) tutorial>python manage.py migrate
>git init
>type .gitignore
*.pyc
/virtualenv
/tutorial/db.sqlite3
>git add -A
>git commit -m "project start"


Django Rest Framework Start and ImageUpload Model Create

settings.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'quickstart',
    'rest_framework',
]
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny',),
    'PAGE_SIZE': 10
}

models.py

class ImageUpload(models.Model):
    title = models.CharField(max_length=100)
    imagefile = models.FileField(null=True)


serializers.py

class ImageUploadSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = ImageUpload
        fields = ('url', 'pk', 'title', 'imagefile')


views.py

class ImageUploadViewSet(viewsets.ModelViewSet):
    queryset = ImageUpload.objects.all()
    serializer_class = ImageUploadSerializer


urls.py

router = routers.DefaultRouter()
router.register(r'imageuploads', views.ImageUploadViewSet)
urlpatterns = [
    url(r'^', include(router.urls))
]


command

(virtualenv) tutorial>python manage.py makemigrations
(virtualenv) tutorial>python manage.py migrate
(virtualenv) tutorial>python manage.py runserver


여기까지 진행하면 http://localhost:8000/ 에 접속해서 작동하는 모습을 볼 수 있습니다.
import 문은 생략하였습니다. 전체 소스코드는 아래 링크를 확인하세요.
https://github.com/kyuhyung-park/djangorestframework_practice/tree/rest_basic

2016년 5월 25일 수요일

Xamarin Android 프로젝트 시작하기

  • Visual Studio 2015 를 실행하고 New Project > Visual C# > Android > Blank 을 선택합니다.
  • 프로젝트 생성시 문제가 있다면 tools > options > xamarin > android settings 에서 jdk, android sdk, android ndk location을 확인합니다.
  • 실행버튼을 눌러 Xamarin Android Player 가 뜨고 앱이 실행되는것을 확인합니다. 단말기가 컴과 연결되어있다면 단말기에서 실행됩니다.
  • Visual Studio 에서 솔루션을 소스컨트롤에 추가 (솔루션디렉토리 우클릭 Add Solution to Source Control) 합니다.
  • git log 해서 보면 .gitignore, .gitattribures 파일을 추가하고 프로젝트 파일을 추가한 것을 확인할 수 있습니다.

2016년 5월 24일 화요일

git pull 시 문제해결

아래는 git pull 을 하면 가끔 발생하는 에러들입니다.
자주 까먹어서 헤메는 내용이라 정리해둡니다.

error: Your local changes to the following files would be overwritten by merge:
        [파일들...]
Please, commit your changes or stash them before you can merge.
error: The following untracked working tree files would be overwritten by merge:
        [파일들...]
Please move or remove them before you can merge.

첫번째 에러는 "Please, commit your changes or stash them before you can merge."
commit 하거나 stash 하라고 하는데요.
저의 경우 git pull 은 보통 원격저장소의 내용으로 덮어 씌우는 경우가 많아서

git stash
git pull

하여 해결합니다.

두번째 에러는 "Please move or remove them before you can merge."
문제가 되는 파일들을 이동하거나 지우라고 하는데요.
git stash 하여도 해결되지 않습니다.
추적하고 있지 않은(untracked) 파일이라 그런것 같습니다.

몇가지 방법이 있습니다.

1. untracked 파일들이라서 그런것 같으니 add 후 stash 합니다.
git add -A
git stash

2. untracked 까지 stash 해주는 옵션을 사용합니다.
이 경우 git stash pop 하면 untracked 였던 파일은 untracked 로 복원됩니다.
git stash --all

3. 워킹 디렉토리 안의 추적하고 있지 않은 모든 파일을 지웁니다.
복원이 안되므로 위 두가지 방법중 하나를 사용하는것이 좋을것 같습니다.
git clean [옵션]


저는 덮어 씌우는 목적이고 외우기 쉬워서

git add -A
git stash
git pull

을 하여 해결합니다.

참고 링크
https://git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-Stashing%EA%B3%BC-Cleaning

2016년 3월 2일 수요일

django 파일 다운로드

이미지 파일을 다운로드 하여 보여주는 코드 예

views.py
from django.http import HttpResponse
from django.http import FileResponse
import os
import pandas
import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame, Series
import StringIO

def index(request):
    return HttpResponse("dir : %s <br><img src='image1'>" % (os.getcwd()))
def image1(request):
    response = FileResponse(open('image.png', 'rb'), content_type='image/png')
    return response


참고링크
https://docs.djangoproject.com/en/1.9/ref/request-response/#django.http.HttpResponse

HttpResponse 를 이용한 구현 예

def image2(request):
    response = HttpResponse(open('image.png', 'rb'), content_type='image/png')
    response['Content-Disposition'] = 'attachment; filename="image2.png"'
    return response

Pandas Series 차트 이미지를 다운로드 하는 예

import pandas
import matplotlib.pyplot as plt
import numpy as np
from pandas import DataFrame, Series
import StringIO
def chart1(request):
    s1 = Series([1,3,2,4,5])
    s1.plot(title="g")
    bufferIO = StringIO.StringIO()
    plt.savefig(bufferIO)
    bufferIO.seek(0)
    response = HttpResponse(bufferIO, content_type='image/png')
    response['Content-Disposition'] = 'attachment; filename="chart.png"'
    return response

2016년 2월 5일 금요일

python anaconda 및 각종 라이브러리 설치


Anaconda
https://www.continuum.io/

conda install ipython 

conda install jupyter

jupyter notebook


conda install django

conda install Flask
http://flask.pocoo.org/

conda install pymysql
https://github.com/PyMySQL/PyMySQL