[django] model.objects.filter를 이용한 검색기능 추가

django에서 model.objects.filter를 이용해 제목, 본문을 검색할 수 있는 기능을 추가하려고 한다.

#index.html

<form class="form-inline" action="{% url 'blog:index' %}" method="get">
    <div class="input-group">
        <select name="fd_name" id="" class="form-control">
            <option value="title">title</option>
            <option value="content">content</option>
        </select>
        <input type="text" class="form-control" name='q'>
        <button class="btn btn-secondary" type="submit">Search</button>
    </div>
</form>
#vew.py

def index(request):
    if request.GET.get('q'):
            variable_column = request.GET.get('fd_name')
            search_type = 'contains'
            filter = variable_column + '__' + search_type
            posts = Post.objects.filter(**{ filter: request.GET.get('q') }).order_by('-published_date')
    return render(request, 'blog/index.html', {
        'posts': posts,
    })

맨처음에는 Post.objdects.filter(requests.GET.get(‘q’)+’__contains’ = requests.GET.get(‘q’))으로 시도를 했지만 에러를 냈다. 문자열로 받아드리기 때문에 장고에서 필드명을 찾지 못하는것 같았다.

modesl.objects.fillter(**kwargs)로 인자를 받기때문에 문자열이 아닌 딕셔너리 **{key:value}로 인자를 전달해서 문제를 해결했다.

참고 사이트
https://docs.djangoproject.com/en/2.1/topics/db/queries/
https://stackoverflow.com/questions/4720079/django-query-filter-with-variable-column
https://wayhome25.github.io/django/2017/05/04/django-queryset-search/

[heroku]로컬db 헤로쿠로db에 덮어쓰기

이전 포스팅에서 헤로쿠 디비를 로컬로 복원하는 방법을 기록했다. 이번에는 반대의 경우를 포스팅 하고자 한다.

환경
1. django, postgreSQL 사용
2 .우분투

로컬에서 dump 파일 생성

PGPASSWORD=mypassword pg_dump -Fc --no-acl --no-owner -h localhost -U myuser mydb > mydb.dump

헤로쿠에 업데이트하기

heroku pg:backups:restore '<SIGNED URL>' DATABASE_URL

‘<SIGNED URL>’ 을 통해서 헤로쿠에 파일을 전달해야 하므로 파일을 웹(깃, 기타 호스팅) 어디가에 저장해놓아야 한다.

참고사이트 : https://devcenter.heroku.com/articles/heroku-postgres-import-export
이전포스팅: 헤로쿠 DB백업 후 로컬로 복원

[heroku] 헤로쿠 DB백업 후 로컬로 복원

헤로쿠 메인페이지
헤로쿠 메인페이지

헤로쿠앱에서 원격으로 작업하지 않고 로컬에서 작업후 업데이트하고자 했음.
로컬에은 sqlite3을 사용하였고 헤로쿠앱은 postgresSQL을 사용하고 있었기 때문에 로컬의 데이터베이스시스템을 변경할 필요가 있었다.

1. 헤로쿠에서 디비파일을 덤프뜨고 로컬로 내려받는다.

heroku pg:backups:capture
heroku pg:backups:download

2. 윈도우용 postgresSQL 설치 후 환경변수 등록
postgresSQl 다운로드
내pc>속성>고급시스템설정>고급>환경변수>시스템변수에서 path 편집>C:\Program Files\PostgreSQL\11\bin경로 추가

3. pgAdmin4에서 사용자 및 디비를 생성한다.
참고주소 : https://www.pgadmin.org/docs/pgadmin4/dev/pgadmin_user.html

4. 디비를 복원한다

pg_restore --verbose --clean --no-acl --no-owner -h localhost -U myuser -d mydb latest.dump

5. 헤로쿠 깃에서 앱 다운로드

heroku git:clone -a myapp

6. setting.py 파일 수정

django와 postgreSQL을 연결하기 위해서  psycopg2 라는 모듈이 필요함 pip list 명령어를 통해서 패키지 확인후 없으면 pip install  psycopg2
설치를 마치면 setting.py 파일의 데이터베이스부분을 수정

setting.py

DATABASES = {
    "default": {
	'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'databsename',
        'USER': 'username',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

7. django 실행

# 정적파일들을 해당앱으로 불러온다
python manage.py collectstatic 

# 로컬서버실행
heroku local web -f Procfile.windows

# 리눅스일경우
heroku local web

# 데이터베이스 마이그레이션
python .managr.py makemigrations
python .managr.py migrate

localhost:5000/admin으로 접속해 데이터 복원을 확인

[django] 페이지 네이션 구현

모듈 로드

from django.core.paginator import Paginator

view.py

import math

from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator
from .models import Post

# Create your views here.
def index(request):
    posts = Post.objects.all().order_by('-published_date')
    paginator = Paginator(posts, 10)
    page = request.GET.get('page')
    contacts = paginator.get_page(page)
    page_range = 5 #페이지 범위 지정 예 1, 2, 3, 4, 5 / 6, 7, 8, 9, 10
    current_block = math.ceil(int(page)/page_range) #해당 페이지가 몇번째 블럭인가
    start_block = (current_block-1) * page_range
    end_block = start_block + page_range
    p_range = paginator.page_range[start_block:end_block]
    return render(request, 'blog/index.html', {
        'contacts': contacts,
        'p_range' : p_range,
    })

start_block = ((current_block-1) * page_range) + 1
end_block = start_block + page_range – 1
위와 같이 계산해 줘야 [1, 2, 3, 4, 5]를 구할 수 있으나 paginator.page_range 배열에서 리스트 슬라이싱을 하기 위해 위 코드처럼 작성 하였다. (리스트는 0부터 시작하기 때문)

list.html

<div class="pagination">
    <span class="step-links">
        {% if contacts.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ contacts.previous_page_number }}">previous</a>
        {% endif %}

        {% for i in p_range %}
            <a href="?page={{i}}" {% if contacts.number == i %}class="active" {% endif %}>{{i}}</a>
        {% endfor %}

        {% if contacts.has_next %}
            <a href="?page={{ contacts.next_page_number }}">next</a>
            <a href="?page={{ contacts.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>

 

 

[php] db데이터 불러와 엑셀파일 만들기

db에 있는 데이터를 엑셀로 뽑아야하는 경우가 있어 구글링하면서 나름대로 방법을 찾아봤습니다.

style 속성을 통해 셀의 배경색, 높이, 넓이 등 스타일을 지정해 줄수도 있습니다.  이미지 위치조정을 위해서 margin이나 left 사용해 봤는데 적용되지 않았습니다.

<?php
include 'wp-config.php';
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
$query = $mysqli->query("SELECT * FROM `wp_posts`");

header("Content-Type: application/xls");
header("Content-Disposition: attachment; filename=file.xls");
header("Pragma: no-cache");
header("Expires: 0");

?>
<table>
    <thead>
        <tr>
            <th>이미지</th>
            <th>제목</th>
            <th>날짜</th>
        </tr>
    </thead>
    <tbody>
        <?php 
        for ($i=0; $row = $query->fetch_array(MYSQLI_ASSOC); $i++) { 
            ?>
            <tr>
                <td style="width:100px; height: 100px;"><img src="http://placehold.it/100x100" alt=""></td>
                <td><?php echo $row['post_title'];?></td>
                <td><?php echo $row['post_date'];?></td>
            </tr>
            <?php
        }
        ?>
    </tbody>
</table>

한글이 깨지는 경우 header값을 수정하고  utf-8 BOM 객체를 생성합니다.

<?php
header('Content-Encoding: UTF-8');
header("Content-Type: application/xls; charset=UTF-8");    
header("Content-Disposition: attachment; filename=file.xls");  
header("Pragma: no-cache"); 
header("Expires: 0");
echo "\xEF\xBB\xBF"; // UTF-8 BOM
?>

[python] 윈도우 스케줄러에서 파이썬 실행시키기

특정시간마다 파이썬파일을 실행시켜야 하는 경우 윈도우 스케줄러에 등록하여 사용할 수 있습니다.

테스트에 사용할 코드

#time.py

import datetime
import os

s= datetime.datetime.now()
isfile = os.path.isfile('log.txt')

if isfile:
    with open('log.txt', 'a') as f: #파일이 있으면 마지막 행에 추가
        f.write(str(s)+'\n')
else :
    with open('log.txt', 'w') as f: #파일이 없으면 log.txt 생성하고 입력
        f.write(str(s)+'\n')

파일이 실행될 때 마다 시간을 입력하는 코드입니다.  코드 준비 되었으니 스케줄러만 등록하면 되겠습니다.

스케줄러 등록방법

1.제어판>관리도구>작업스케줄러 실행합니다.
2.작업 만들기 실행
3.일반탭 : 이름, 설명 등록, 보안옵션에서 가장 높은 수준의 권한으로 실행 활성화
3.트리거 :  새로만들기 클릭(원하는 설정값 세팅)
4.동작 : 새로만들기 클릭 설정항목에서 프로그램/스크립트, 인수추가, 시작위치 항목을 설정합니다.

*설정 예)
-프로그램/스크립트 : C:\Users\jisung\AppData\Local\Programs\Python\Python36-32\python.exe
-인수 추가 : C:\Users\jisung\Desktop\python\time.py
-시작 위치 : C:\Users\jisung\Desktop\python

5.조건, 설정 항목 설정

스케줄러등록 후 log.txt파일이 생성되고 자료가 입력이 되었는지 확인해 보면 정상적으로 스케줄러가 잘 작동하는 것을 확인 할 수 있었습니다.

 

 

 

[python] selenium 으로 geckodriver 실행

Selenium 문서를 보다가 파이어폭스 드라이버를 이용해서 브라우저를 제어하려고 하는데 ‘geckodriver’ executable to be in path 라는 메세지를 띄어서 구글링함

from selenium import webdriver
driver = webdriver.Firefox(executable_path='C:\yourpath\geckodriver.exe')
driver.get('https://github.com')

크롬드라이버를 사용할 경우

from selenium import webdriver
driver = webdriver.Chrome('C:\yourpath\chromedriver.exe')
driver.get('https://github.com')

 

[python] Requests 라이브러리 사용법

평소 크롤링에 관심이 있어 파이썬에 기웃거리다가 알게된 내용을 정리해봅니다.Requests 라이브러리는 해당 주소로 요청을 보내면 응답을 받도록 해주는 HTTP 클라이언트 입니다.

1.설치 및 모듈로드

#pip를 이용한 모듈설치
pip install requests
 
#requests 모듈 로드
>>>import requests

 

2.사용방법

*요청보내기

HTTP method GET, POST, PUT, DELETE, OPTION을 요청할 수 있습니다.

>>> r = requests.get('https://api.github.com/events', param = {'key':'value'})
>>> r = requests.post('https://httpbin.org/post', data = {'key':'value'})
>>> r = requests.put('https://httpbin.org/put', data = {'key':'value'})
>>> r = requests.delete('https://httpbin.org/delete')
>>> r = requests.head('https://httpbin.org/get')
>>> r = requests.options('https://httpbin.org/get')

 

* 복잡한 요청

튜플이나, 딕셔너리 형태로 데이터를 전송해야하는 경우가 있을 수 있습니다.
아래의 코드중에 ‘payload_tuple’ 변수를 post로 전송한 뒤 r1.text을 확인해 보면 형태가 변경 되어 응답 된 것을 확인 할 수 있습니다.

>>> payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
>>> r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
>>> payload_dict = {'key1': ['value1', 'value2']}
>>> r2 = requests.post('https://httpbin.org/post', data=payload_dict)
>>> print(r1.text)
{
  ...
  "form": { 
    "key1": [
      "value1",
      "value2"
    ]
  },
  ...
}
>>> r1.text == r2.text
True

이러한 형태의 변환 없이 요청을 보내려면 json형식으로 변환하여 보내면 되겠습니다.
보내는 방법은 데이터 자체를 json으로 인코딩 해서 보내는방법과 v2.4.2버전 부터 에 추가된 json 파라메터를 이용하는 방법입니다.

– json으로 인코딩하기

>>> import json
 
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'} 
>>> r = requests.post(url, data=json.dumps(payload))

-json파라메터 이용하기

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
 >>> r = requests.post(url, json=payload)

*파일보내기

>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
 
>>> r = requests.post(url, files=files)
>>> r.text
{
  ...
  "files": {
    "file": "<censored...binary...data>"
  },
  ...
}

 

*응답컨텐츠

응답은 requests.text 또는 requests.content로 확인 할 수 있습니다. 이 방법의 차이는 전자는 텍스트 형식이고 후자는 바이트형식입니다.
또한 requests.encoding으로 인코딩을 확인 할 수 있으며 인코딩 변환은 requests.encoding = ‘utf-8’ 같이 원하는 형식을 지정해주면 됩니다.
>>> import requests
 
>>> r = requests.get('https://api.github.com/events')
>>> r.text
u'[{"repository":{"open_issues":0,"url":"https://github.com/...
...
...
...'
 
>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'

 

*응답상태코드

응답에 서공하면 상태코드 200을 리턴합니다.
(400번대 코드를 리턴하면 클라이언트 에러, 500번대를 이런하면 서버측 에러입니다.)
>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code
200
>>> r.status_code == requests.codes.ok
True
 
>>> bad_r = requests.get('https://httpbin.org/status/404')
>>> bad_r.status_code
404
 
>>> bad_r.raise_for_status()
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error

만약 status_code가 200이면 raise_for_status()를 사용하면 빈값을 리턴합니다.

*헤더변경

>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)

 

*헤더응답

>>> r.headers
{
    'content-encoding': 'gzip',
    'transfer-encoding': 'chunked',
    'connection': 'close',
    'server': 'nginx/1.0.4',
    'x-runtime': '148ms',
    'etag': '"e1ca502697e5c9317743dc078f67693f"',
    'content-type': 'application/json'
}
 
>>> r.headers['Content-Type']
'application/json'
 
>>> r.headers.get('content-type')
'application/json'

 

* 쿠키

만약 응답에 쿠키가 포함 되어있다면 아래와 같이 접근할 수 있습니다.

>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
 
>>> r.cookies['example_cookie_name']
'example_cookie_value'


출처: https://yoojisung.tistory.com/13 [개발자 유지성의 블로그]

반대로 서버에게 쿠키를 보내야할 경우 cookies 파라메터를 이용합니다.

>>> url = 'https://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
 
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'

 

*리다이렉션 과 히스토리

경우에 따라서 해당페이지에서 리다이렉션이 되는 것을 방지하고자 할때 allow_redirects 매개변수를 사용합니다.

>>> r = requests.get('http://github.com/', allow_redirects=False)
 
>>> r.status_code
301
 
>>> r.history
[]
 
>>> r = requests.head('http://github.com/', allow_redirects=True)
 
>>> r.url
'https://github.com/'
 
>>> r.history
[<Response [301]>]

 

*타임아웃

timeout 매개변수를 사용해서 설정한 시간이 지나면 응답을 종료합니다. 응답이 돌아올때까지 서버가 일을 하는 것을 방지 하는 목적같습니다.

>>> requests.get('https://github.com/', timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

requests 공식 사이트

http://docs.python-requests.org/en/master/