뷰와 템플릿

뷰를 이용해서 HTML코드를 렌더링하기

  1. from django.shortcuts import render 를 추가한다 (/polls/views.py)
  2. HTML파일을 생성한다.
  3. views.py의 함수 ⇒ 페이지에 반환 값을 변경한다.
context = {'questions':latest_question_list}
return render(request, 'polls/index.html',context)

인자는 context처럼 넘겨줄 수 있는데 이 때 HTML에서는 넘겨받은 인자를 {{args}} 처럼 사용한다.

{{question}}

제어문 사용하기

인자가 여러개인 경우 1개를 출력하고자 할 때는 .index의 형태로 사용한다.

{{question.0}}
{{question.1}}
{{question.2}}
  • 반복문으로의 활용은 다음과 같다.
 {% for q in questions%}
    <li>{{q}}</li>
 {% endfor %}
  • IF문은 다음과 같다.
{% if questions %}
<ul>
    ...
</ul>
{% else %}
<p>no questions</p>
{% endif %}

Detail 설정하기

Path에 숫자를 넣어서 그 값을 인식시켜 페이지를 수정하고자 한다.

  1. /polls/urls.py에서 path를 추가한다.
# 수를 입력받아서 question_id로 사용한다.
path('<int:question_id>',views.detail, name = 'detail'),
  1. /polls/views.py에서 render하는 메서드 생성하기
def detail(request,question_id):
    question = Question.objects.get(pk=question_id)
    return render(request, 'polls/detail.html',{'question':question})
  1. detail.html생성 : 인자들을 넘겨 받아서 간단하게 다음과 같이 생성하였다.
# detail.html
<h1>
    {{question.question_text}}
</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{choice.choice_text}}</li>

{% endfor %}

링크를 사용해서 페이지 이동.

  • 태그를 활용하여 이동시키기 -> a태그 사용
  • question.id를 활용해서 href 링크를 걸어주면 된다.
{% for q in questions%}
    <li><a href="/polls/{{q.id}}">{{q.question_text}}</li>
{% endfor %}
  • 코드를 통해서 얻어오는 방법은 urlpattern의 name을 통해서 가져올 수가 있다.
# urls.py
app_name = 'polls' # app_name을 지정한 경우 app_name:name형식으로 불러와야한다.
urlpatterns = [
    path("", views.index, name = 'index'),
    path('<int:question_id>',views.detail, name = 'detail'),
    path("some_url", views.some_url),
]

# index.html
{% for q in questions%}
        <li><a href="{% url 'polls:detail' q.id %}">{{ q.question_text }}</a></li>
{% endfor %}

에러 처리하기

try,except 구문을 사용해도 가능하나 Django에서는 Shortcut을 통해 404 에러메세지를 제공한다.

from django.shortcuts import render,get_object_or_404
# 기존
question = Question.objects.get(pk=question_id)
# 변경
question = get_object_or_404(Question, pk =question_id)

폼과 커스터마이징

: 값을 제출할 용도로 사용한다.

폼을 추가하는 순서에 대해서 간단하게 설명한다.

  1. path를 추가하기. 값이 보내져서 path값이 변경되는 경우 추가해주어야 한다.
# urls.py
path('<int:question_id>/vote/', views.vote, name ='vote'),
  1. views에 메서드 생성
    여기서 주목할 점은 입력받은 값을 통해서 db와 연결하는 부분이다.
    다음 처럼 request.POST를 활용해서 name이 choice인 값을 받을 수 있다.
selected_choice = question.choice_set.get(pk = request.POST['choice'])

# detail.html에서의 name
<input type = 'radio' **name = "choice"**, id = "choice{{forloop.counter}}" value="{{choice.id}}">

# 서버에 값을 저장시키고 싶을 때에는 save를 사용한다.
selected_choice.save()
  1. form 제작하기
    : 아까 제작해두었던 vote path로 값을 post 한다.
<form action="{% url 'polls:vote' question.id %}" method ='post'>

두개의 서버에서 요청하는 경우

F 메서드를 이용해서 DB에서 값을 받고 다시 수행한다.

from django.db.models import F
selected_choice.votes = F('votes') + 1

어드민 페이지 커스터마이징

  • admin.py에서 수정한다.
  • 값을 읽기전용으로만 하고싶은 경우에는 다음처럼 readonly_fields를 걸어준다.
# admin.py
class QuestionAdmin(admin.ModelAdmin): 
    fieldsets = [
        ('질문 섹션',{'fields':['question_text']}),
        ('생성일',{'fields':['pub_date']}),
    ]
    readonly_fields = ['pub_date']
  • question에서 choice option을 관리하는 방법 만들기.
class ChoiceInLine(admin.TabularInline):
    model = Choice
    extra = 3

class QuestionAdmin(admin.ModelAdmin): 
    fieldsets = [
        ('질문 섹션',{'fields':['question_text']}),
        ('생성일',{'fields':['pub_date']}),
    ]
    readonly_fields = ['pub_date']
    inlines = [ChoiceInLine]

  • 숨김처리는 클래스 값을 추가하여 구현한다.
('생성일',{'fields':['pub_date'],'classes':['collapse']}), 

목록을 보기 좋게 만들기.

  • admin에서 목록에 column추가하기 : list_display = ('question_text','pub_date') 이렇게 한줄 추가하면된다.

  • 컬럼의 레이블을 바꿔주고싶으면 verbose_name을 추가한다.
# models.py
class Question(models.Model):
    question_text = models.CharField(max_length=200 , verbose_name='질문')
    pub_date = models.DateTimeField(auto_now_add=True, verbose_name='생성일')

# class로 정의 한 값인 경우
from django.contrib import admin
@admin.display(boolaen=True,description='최근 생성 (하루기준)') # boolean을 하면 아이콘으로 표시된다.
  • 검색 기능

리스트 필터는 list_filter = ['pub_date'] 을 추가하여 사용한다. → 장고가 타입에 맞춰서 옵션들을 제공한다.

검색 필드는 search_fields = ['question_text', 'choice__choice_text'] 를 추가한다. → 리스트를 활용하여 옵션들을 넣을 수 있다.

'Python > Django' 카테고리의 다른 글

Django - apscheduler  (0) 2024.03.15
Django - User  (0) 2024.03.15
Django - Serializers, CRUD  (0) 2024.03.15
Django - 설치 및 기본 세팅  (2) 2024.03.15

개발 환경 설치하기.

py -m venv my_project
# 프로젝트가 정상적으로 설치되면.
project-name\Scripts\activate.bat # 을 통해서 실행
# 가상환경 비활성화를 하고 싶다면 
# deactivate
  • Django 설치
py -m pip install Django
# 버전 체크
django-admin –version

Django 프로젝트 생성과 실행

  • 생성
$ django-admin startproject {project_name}
  • 실행
$ python manage.py runserver

앱 생성하기

  • polls 라는 이름의 앱을 생성하기
$ python manage.py startapp polls
  • url 추가하기

polls 폴더안에 urls.py 파일을 생성해준 후에 path를 정의했다.

from django.urls import path

urlpatterns = [
    path("", views.index, name = 'index'),
]

 

그리고 mysite/urls.py에 path추가 하기!

from django.contrib import admin
from django.urls import path, include #기본 구조에 include를 추가로 import

urlpatterns = [
    path('polls/', include('polls.urls')), #polls/ 에 대한 path추가
    path("admin/", admin.site.urls),
]

 

마지막으로 views.index 생성해서 테스트

# polls\views
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello world.")
# polls\urls
from . import views # 추가

 

이제 url를 찾아서 확인을 해보면 다음과 같이 나오게 된다. http://127.0.0.1:8000/polls/


모델 만들기

모델은 DB를 테이블별로 읽어서 하나의 테이블에 저장된 값을 코드에서 읽도록 도와준다 (ORM)

 

1. Models에 모델(테이블) 생성하기

# 질문에 대한 선택을 하는 모델을 생성했다.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question,on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

 

모델을 테이블에 사용하기 위한 마이그레이션 생성. 마이그레이션 생성을 위해 다음과 같은 작업들이 필요하다.


1-1. mysite/settings.py의 INSTALLED_APPS 수정: 이곳에 polls.apps의 class값을 추가해야 한다.

1-2. migration 파일 생성: $ python manage.py makemigrations polls

1-3. migration 실행 $ python manage.py migrate id 컬럼이 자동생성된다. → 인덱싱을 위해 사용

 

만들어진 테이블 확인하기./mysite/sqllite3 db.sqllite3 를 통해 db에 접근하여 테이블 확인이 가능하다.

 

되돌리기 : python manage.py migrate polls 0001 를 사용해서 이전 버전으로 마이그레이션을 되돌리는 것이 가능하다.
→ 마이그레이션을 기록을 참고해야한다.
→ 버전을 돌린 후에 마이그레이션을 지우면 된다. /polls/migrations + 모델에서 생성한 필드들을 제거해야한다.

 

참고


Django Admin

관리자 계정 생성하고 접속하기.

  • admin계정 만들기: python manage.py createsuperuser 를 통해 계정만들기를 시작할 수 있다.
  • 로그인하는 법: 서버 실행 후 /admin으로 접속한다.

모델 등록하기

  • /polls/admin.py에 원하는 모델을 등록해주어야 한다.
# /polls/admin.py
from .models import *
admin.site.register(Question)

 

question object 대신 이름을 사용하고 싶은 경우 각 클래스에 str을 설정해주어야하는데 코드 예시는 다음과 같다.

# /polls/models.py
class Question(models.Model):
	question_text = models.CharField(max_length=200)
	...
	def __str__(self):
		return self.question_text # 또는 str 값으로 리턴해도 가능하다.

 


Django Shell

  • 실행 : python manage.py shell

모델 접근

  1. import 하기: 원하는 모델을 우선 가져온다. 예시) from polls.models import *
  2. object 가져오기: Model.objects.all() 의 형태로 오브젝트를 가져올 수 있으며, 변수에 할당 가능하다.
  3. object 안에서의 참조: ForeignKey 가 있는 경우 해당 키를 통해서 연결된 다른 테이블의 값도 가져올 수 있다.
    그러나 ForeignKey가 없는 경우에는 model.sub_model_set.all() 처럼 _set을 사용해서 가져와야 한다.

현재 시간 구하기

  • TimeZone이 필수적으로 들어가야한다.
# timezone을 활용하여 현재 시간 구하기
from django.utils import timezone
timezone.now()
# => datetime의 형태로 출력되며, tzinfo가 붙어서 나오게 된다.

레코드 생성하기

: shell을 통해서 레코드를 생성하는 방법

예제) Question테이블을 Choice 테이블이 참조한다.

  1. 상위 테이블에서 하위 테이블을 따라가면서 생성
# 1. save를 이용
q = Question(question_text = '테스트 레코드')
q.save() # save를 하지 않으면 db에 적재되지 않는다, 메모리에만 존재
# 필수인 값을 넣어주지않으면 에러가 발생한다.
# 값을 추가하는 방법은 다음과 같다.
q.pub_date = timezone.now() # 이러한 방법

# 2.create를 이용
q.choice_set.create(choice_text='a')
# 이 경우에 db에 바로 적재된다.

# 참고로 model의 field 인자에 auto_now_add를 입력하면 생성 시간을 자동으로 입력 가능하다.
  1. 하위테이블에서 ForeignKey값을 붙여서 레코드를 생성
choice_c = Choice(choice_text='c',question=q)
choice_c.save()

레코드 수정 및 삭제

  • 수정 : 수정은 해당 인자의 값을 받아 수정을 거친 후 save()를 활용하여 db에 연동시킨다.
  • 삭제 : 지우고자 하는 값을 가져온 후 .delete()를 사용하여 제거한다. db에 바로 연동된다.
    연결되어 있는 기존의 모든 오브젝트를 제거하는 경우 _set.delete() 처럼 _set을 같이 사용한다.

모델 필터링

  • 조건에 맞는 값 1개를 가져오고 싶은 경우: .get() 메서드를 사용한다.
  • 여러개의 쿼리셋을 가져오고 싶은 경우: .filter() 메서드를 사용한다.
  • 각 인자의 상세값은 __조건 을 통해서 가져올 수 있는데 예시 몇가지를 본다면 다음과 같다.
# question_text가 a로 시작하는 값
Question.objects.get(question_text__startwith='a') 
# a가 포함된 값을 찾으면
Question.objects.get(question_text__contains='a') 
# 보다 큰 값은 __gt
# 특정 시간으로 필터링
Question.objects.filter(pub_date__second=25)
# 이것을 쿼리문으로 변경해서 보고싶다면 다음과 같이 사용한다. .query 사용
q = Question.objects.get(pk=1)
print(q.choice_set.all().query)

: 조건들은 docs의 QuerySet method refernce를 참고한다. QuerySet method reference

  • 추가로 조건에 맞는 값들 뒤에 .update() 를 이용해서 값을 업데이트 할 수 있으며, delete도 가능하다.
  • filter 또한 여러번 걸 수 있다.
  • regex : 정규 표현식 ⇒ 대상이 정규표현식을 만족하는 경우 필터링 가능하다.
    정규표현식 참고 : https://hamait.tistory.com/342
# 코드 예시
q= Question(question_text = '휴가 계획이 있나요?")
q.save()

# 정규 표현식을 이용해서 '휴가'가 처음에 오고 '어디'라는 단어가 오는 값만 필터링한다.
Question.objects.filter(question_text__regex=r'^휴가.*어디')

관계 기반 필터링

  • 제외하고 필터링하고 싶은 경우에는 filter 대신 exclude() 메서드를 사용한다.
  • foreignkey를 갖는 경우에 그 테이블의 속성을 참조해서 필터링을 걸 수 있다.
Choice.objects.filter(question__question_text__startswith='휴가')
Choice.objects.exclude(question__question_text__startswith='휴가')

모델 메서드

  • 추가 : models에 있는 각 클래스에 메서드를 추가해주면 된다. 기존 파이썬 메서드 방식과 동일하다.

'Python > Django' 카테고리의 다른 글

Django - apscheduler  (0) 2024.03.15
Django - User  (0) 2024.03.15
Django - Serializers, CRUD  (0) 2024.03.15
Django - View, template, form  (0) 2024.03.15

웹 스크래핑을 위한 python 라이브러리 실습 코드이다. bs4와 selenium에 대해서 간단한 실습코드들을 정리했다.

BeautifulSoup4 실습

Install

pip install beautifulSoup4

객체 생성

import requests
from bs4 import BeautifulSoup

res = request.get("http://example.com")
soup = BeautifulSoup(res.text,"html.parser")

# 보기 편한 출력은 prettify를 사용한다.
# print(soup.prettify())

태그 가져오기 - find

요소를 하나 찾고 싶은 경우 find, 여러개의 경우 find_all을 사용한다.

soup.find("h1")
results=soup.find_all("p")

Locator를 활용하기 - id, class

# id를 이용해 요소 가져오기
soup.find("div",id = "results")
# class를 이용해 요소를 가져오기
result = soup.find("div",class = "page-header")

# text 값을 출력
# result.h1.text.strip()

페이지네이션

페이지가 많은 경우 url을 통해 page를 변경 가능하다. 따라서 request.get을 반복적으로 수행하면서 값을 가져오면 된다.

for i in range(1,10):
	res = request.get(f"http://example.com/page={i}")

동적 웹페이지

  • 정적 웹페이지 : HTML 내용이 고정된 사이트를 말한다.
  • 동적 웹페이지 : HTML 내용이 변경되는 사이트. JS를 주로 사용한다.
    =>동적 웹페이지의 경우 비동기 처리가 이루어지기 때문에 데이터의 처리가 늦어지는 경우 데이터가 완전하지 않은 경우가 발생한다.
    파이썬에서는 Selenium을 활용하여 이러한 웹 페이지들을 스크래핑한다.

Selenium 라이브러리

: selenium은 Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크이다.

설치

: 라이브러리 설치와 webdriver-manager를 미리 설치해두고 사용한다.

# 주피터 환경에서 설치
%pip install selenium
%pip install webdriver-manager
# 아나콘다에 설치
conda install -c conda-forge selenium

크롬창 띄우기

WebDriver 모듈을 이용하여 크롬창을 띄우는 방법이다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
# 다음 코드를 통해서 드라이버를 같이 불러온다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("http://www.example.com")

# with-as 버전
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")

요소 찾기 find_element

: By와 find_element를 활용하여 페이지 내 요소를 찾을 수 있다.

  • .find_element(by, target) : 하나
  • .find_elements(by, target) : 여러개
# p태그를 찾는 예시
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")
    print(driver.find_element(By.TAG_NAME, "p").text)

Wait

: 동적 페이지를 스크래핑하기 위해서는 페이지 로딩시간을 기다리는 것이 필요한 경우가 존재한다.

  • Implicit Wait : 암시적 기다림, 로딩이 다 될 때까지의 한계 시간을 의미한다. driver.implicitly_wait(5)
  • Explicit Wait : 명시적 기다림, until 메서드를 활용해서 target 요소가 존재할 때 까지 기다린 후 다음 명령을 수행한다.
from selenium.webdriver.support import expected_conditions as EC
# 요소가 존재하면 그 요소를 반환한다.
element = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH,'')))

이벤트 처리 (마우스,키보드)

: ActionChains를 활용하여 마우스와 키보드 입력과 같은 동작을 수행할 수 있다.

from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver import Keys, ActionChains

# 버튼 클릭
button = driver.find_element(By.XPATH,'')
ActionChains(driver).click(button).perform()

# input 요소에 값 전달.
text_input = driver.find_element(By.XPATH,'')
ActionChains(driver).send_keys_to_element(text_input, "input_text").perform()

+ Recent posts