반응형

Oracle DB의 테이블을 이용해서 Join하는 방법을 알아보겠습니다.

 

1. 모델 정의

1:N관계에서 N쪽 테이블(TempDataroomHstry클래스)에 ForeignKey로 1쪽 테이블명(Temp래스)을 지정해줍니다. 이때, 1쪽 테이블의 참조하려는 필드가 Primary Key로 지정되어있으면 상관없지만, 없을 경우 필드 정의에(ForeignKey 함수 내부에서) to__field='목표필드' 를 지정해줘야 합니다. 그리고, db_column='참조칼럼명' 에서 해당 테이블에서 참조할 실제 칼럼(필드)명을 지정해줘야 합니다.

 

(*) Oracle DB에서만 이런 문제가 발생하는 것인지.. 아직은 잘 모르겠습니다. 이것 때문에 한참을 헤메었네요..^^;; 또한 이번의 경우 Temp의 emp_field와 TempDataroomHstry의 id는 사실 칼럼명은 동일하게 'emp_#'이었습니다. db_column에 넣어주는 값이 현재 테이블의 칼럼명인지, 목표 테이블의 칼럼명인지 좀 헷갈립니다...

<models.py>

from django.db import models


class Temp(models.Model):
    emp_field = models.CharField(db_column='emp_#', primary_key=True, max_length=7)  # Field renamed to remove unsuitable characters. Field renamed because it ended with '_'.
    emp_x = models.CharField(max_length=2, blank=True, null=True)
    kornm_n = models.CharField(max_length=32, blank=True, null=True)    
    res_1 = models.CharField(db_column='res_#1', max_length=12, blank=True, null=True)  # Field renamed to remove unsuitable characters.    
    sex_n = models.CharField(max_length=2, blank=True, null=True)    
    dept_c = models.CharField(max_length=16, blank=True, null=True)
    
    class Meta:
        managed = False
        db_table = 'TEMP'


class TempDataroomHstry(models.Model):
    seq_field = models.IntegerField(db_column='seq_#',primary_key=True)  # Field renamed to remove unsuitable characters. Field renamed because it ended with '_'.
    id = models.ForeignKey(Temp, to_field='emp_field', db_column='emp_#',on_delete=models.CASCADE, null=True, related_name='id')   #, related_name='tempdataroomhstry'
    in_d = models.DateField(blank=True, null=True)


    class Meta:
        managed = False
        db_table = 'TEMP_DATAROOM_HSTRY'

 

2. 데이터 활용하기

Join을 위해서는 "select_related()"나 "prefetch_related()"를 사용하는데, 이번에는 select_related()만 알아보도록 하겠습니다. select_related는 1:1 또는 1:N 의 경우에 사용할 수 있는 함수입니다. (정방향 참조필드). select_related()의 인자로는 해당 Table의 ForeinKey를 넣어줍니다. 아래의 예제에서는 우선 'id'칼럼을 이용하여 join후 모든 데이터를 불러오고 'in_d'를 기준으로 역정렬(desc)을 하여 list를 만들고, 이를 home.html에 넘겨줍니다.

<views.py>

from django.core.paginator import Paginator
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse

from .models import TempDataroomHstry

#### 초기화면 및 조회함수 ####
def index(request):
    list = {}
    list = TempDataroomHstry.objects.select_related('id').all().order_by('-in_d')

    # HTML에서 인자가 전달될 경우 처리
    q = request.GET.get('q', '')
    if q:
        list = list.filter(id=q)
    # 여기까지
   
    context = {'member_list': list}
    return render(request, 'home.html', context)

A테이블에 B가 조인될 경우 "[A].[조인된A칼럼명].[B칼럼명]"과 같이 사용하면 됩니다. 아래 home.html파일은 실제 Join된 데이터를 불러와 사용하는 예시를 볼 수 있습니다.

<home.html>

....
<tbody>
{% if member_list %}
  {% for member in member_list %}
    <tr>
      <td>{{ member.id.emp_field }}</td>
      <td>{{ member.id.kornm_n }}</td>
      <td>{{ member.id.res_1 }}</td>
      <td>{{ member.id.sex_n }}</td>
      <td>{{ member.id.dept_c }}</td>
      <td>{{ member.in_d }}</td>
    </tr>
  {% endfor %}
{% endif %}
</tbody>

 

~~~끝~~~

반응형
반응형

간단히 Database를 조회/입력할 수 있도록 Form 을 구성하겠습니다. 작동방식은, 최초 접근 시 아래와 같이 DB의 데이터를 보여줍니다.

1. 초기화면

텍스트 필드에 ID(1, 2, 3..)을 입력하면 해당하는 결과만 나타나게 합니다.

2. 검색화면

마지막으로, 아래의 데이터 입력폼에 데이터를 입력하면 데이터가 등록되면서, 다시 초기화면에서 전체 리스트를 출력합니다.

3. 데이터 입력화면

데이터베이스에는 Member 테이블이 있고, 인원을 몇명 넣어줄 예정입니다. 필드는 ID, Name, Email이 있습니다.

 

1. 프로젝트 구성

프로젝트 구성은 아래와 같습니다. Django 프로젝트 / 앱생성이나 기본 설정은 Django 시작하기 다른 포스트를 참조하시기 바랍니다.

pip install django // 장고 설치

django-admin startproject mysite .  // mysite 프로젝트 시작

django-admin startapp members // members 앱 작성

<mysite/settings.py>

 

2. members/models.py

Members 모델클래스를 최대한 간단히 정의하겠습니다. Database는 sqlite를 사용하고, 제 프로젝트에서는 db폴더에 저장해놓았습니다. (하단 settings.py참조)

from django.db import models

# Create your models here.
from django.db import models

# Create your models here.
class Members(models.Model):
    id = models.IntegerField('ID', primary_key=True, auto_created=True)
    name = models.CharField('NAME', max_length=50)
    email = models.CharField('EMAIL', max_length=100, blank=True)

    def __str__(self):
        return self.name

 

3. members/admin.py

Members 데이터를 admin 사이트에서 조작 가능하도록 등록해줍니다.

from django.contrib import admin

# Register your models here.
from members.models import Members

admin.site.register(Members);

 

4. 데이터 Migration

python manage.py makemigrations

python manage.py migrate

 

5. templates/home.html파일 작성

조회시, form의 action url에 "."만 작성하면 동일한 url에서 처리하게 되고, 현재의 페이지에서 이동없이 데이터만 새로고침 해 줍니다. 따라서 "/members/" url에서 데이터를 처리합니다.

데이터 입력시에는 "/members/add" url에서 데이터를 처리합니다. 

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />

<!-- 조회폼 -->
<form action="." method="get">
    {% csrf_token %}
    <input type="text" name="q" value="{{ q }}">
    <input type="submit" value="Submit">
</form>

{% if member_list %}
<ul>
    {% for member in member_list %}
    <li><a href="/members/{{ member.id }}">{{ member.id }} &nbsp {{ member.name }} &nbsp {{ member.email }}</a></li>
    {% endfor %}
</ul>
{% else %}
<p><li>No members are available.</li></p>
{% endif %}

<!-- 입력폼 -->
<form action="{% url 'members:add' %}" method="post">
    {% csrf_token %}
    <input type="text" name="id" value="{{ id }}">
    <input type="text" name="name" value="{{ name }}">
    <input type="text" name="email" value="{{ email }}">
    <input type="submit" value="Submit">
</form>
</body>
</html>

6. members/urls.py

"/members/"를 처리하기위한 url을 작성합니다. 이 url로 요청이 들어오면 views.index 함수에서 처리합니다.

from django.contrib import admin
from django.urls import path

from . import views

app_name='members'

urlpatterns = [
    path('', views.index, name='home'),
    path('add/', views.add, name='add'),
]

members 앱의 url은 mysite/urls.py의 서브 url로 정의되어있어야 합니다.

<mysite/urls.py>

from django.contrib import admin
from django.urls import path, include

from mysite.views import HomeView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', HomeView.as_view(), name='home'),
    path('members/', include('members.urls')),  // 잊지말고 추가!!
]

 

7. members/view.py 

조회함수는 초기 화면구성 함수인 index에 함께 구성합니다. 기본은 모든 members리스트를 다 보여주고, 조회조건이 있을 시에는, 데이터를 GET방식으로 가져와 query하여 결과를 context에 실어서 보여줍니다.

 

입력함수는 별도로 add라는 함수를 구성하였습니다. index.html의 입력폼에서 id/name/email을 가져와 Member 클래스의 객체를 생성하고, 다시 home url로 보냅니다. 그러면 초기 화면에서는 전체 리스트를 보여줍니다.

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse

from members.models import Members

#### 초기화면 및 조회함수 ####
def index(request):
    list = Members.objects.all()
    # HTML에서 인자가 전달될 경우 처리
    q = request.GET.get('q', '')
    if q:
        list = Members.objects.filter(id=q)
    # 여기까지

    context = {'member_list': list}
    return render(request, 'index.html', context)

#### 입력함수 ####
def add(request):
    id = request.POST.get('id','')
    name = request.POST.get('name','')
    email = request.POST.get('email', '')

    member_add = Members(id=id, name=name, email=email)
    member_add.save(force_insert=True)

	# 데이터 입력 후에는 첫페이지 url로 다시 보냅니다.
    return HttpResponseRedirect(reverse('members:home'))

 

참고로, 폴더 구성/인식을 위한 기본 세팅을 아래에 적어놓습니다.

<참고: mysite/settings.py>

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 3.2.9.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-t)05#=7yvsemt_tyu9dksl7j$$br(7wfgyguds3@#-bj5t$m+7'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'members.apps.MembersConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'], # 수정
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db' / 'db.sqlite3', # 수정
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'Asia/Seoul' # 수정

USE_I18N = True

USE_L10N = True

USE_TZ = False # 수정


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

### 추가 ###
STATICFILES_DIR = (BASE_DIR/'static',)
# STATIC_ROOT = # 배포시 사용

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR/'media'

# AUTH_USER_MODEL =
# LOGGING =

~~~끝~~~

반응형

'Programming > Python_Web' 카테고리의 다른 글

Flask - mysql 연동  (0) 2020.08.21
Flask - apache 연동(mod_wsgi)  (0) 2020.08.21
Django - Oracle연동 (11g), inspectdb 사용법  (3) 2020.05.21
Django - IIS에서 static file 설정하기  (0) 2020.05.14
Django - CSS적용하기  (0) 2020.05.13
반응형

아...힘든 하루네요.

회사에서 쓰는게 oracle 11g 2Release 32bit라서 이걸 모델로 집에서 django랑 연동 테스트 해보려고 삽질을 시작했는데... 오전 9:30분에 시작해서 지금, 오후 10:00에서야 드디어 완성했습니다. 그것도 완전한 완성이라고 볼 순 없고 부분적으로 모자란 부분이 있습니다.

 

오늘 최종적으로 성공한 각 프로그램/모듈의 버전은 아래와 같습니다.

  oracle 11g 2Release 64bit

  Python 3.6.1 64bit

  Django 1.11.22

  cx_Oracle 6.3

 

32비트고 뭐고 왔다갔다 하느라 정신없어서..64비트로 우선 통일!

안되는 영어로 계속 뒤져보니까 요즘나오는 Django 3.0이나 2.X대 버전은 oracle 11g를 지원을 안한다고 합니다. 가능한 버전이 Django 1.11.XX대 버전이라고 합니다. 문제는 얘도 Python 버전을 가리는데..최신 3.8은 안되고 2.7 ~ 3.6 까지만 된다네요.... 버전맞추기 참 힘듭니다. cx_Oracle은 나중에 알아보니 그냥 최신 깔아도 될 것 같습니다. 여기까진 그냥 고생한 내용이고..

현재는 버전에 크게 상관없이 잘 구동이 됩니다.

 

Django와 Oracle 연동하는 방법을 알아보겠습니다. 피곤해서 캡쳐는 생략하겠습니다. (나중에 시간나면 업뎃하는걸로..)

 

1. 버전 확인. 오라클과 연동할 때 같은 32bit 또는 64bit인지 꼭 확인합니다.

=> 64bit를 기본으로 깔면 32bit와 연동할 때에도 큰 문제없이 가능합니다.

 

2. oracle 11g와 연동할 django 1.11.22버전 설치합니다.

  - pip install django~=1.11.22

(특정 버전을 골라 설치하려면 이렇게 해도 되지만, Django 3.0부터는 ASGI를 지원하므로, 굳이 구버전을 설치할 필요는 없습니다.)

 

3. DB연동을 위한 cx_Oracle을 설치합니다.

  - pip install cx_Oracle

 

4. 프로젝트 생성

  - django-admin startproject mysite (새로 생성되는 mysite폴더에 manage.py가 생성됩니다. 귀찮아짐..)

  - django-admin startproject mysite . (마지막에 . 을 찍어주면 현재 폴더에 manage.py가 생성됩니다.)

 

5. 앱 생성

  - python manage.py startapp polls

 

6. Database 접속정보 수정

 

 

 

7. inspectdb를 이용하여 models.py생성

  - python manage.py inspectdb test>polls/models.py

  - error발생 가능: TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'

  - Django 버전을 꼭 확인하시기 바랍니다. Django 2.0이상 버전은 oracle11g와 안맞는다네요...가끔 했다는 분들도 계신데, 그냥 고수이신걸로...ㅠㅜ

 

8. python manage.py migrate --fake-initial (기존 Oracle 데이터가 있을 경우 불요)

  - error발생 가능: django.db.utils.DatabaseError: ORA-00955: name is already used by an existing object

  - 최초 migration한 파일이 남아있을 수 있으니 지우고 다시

    python manage.py makemigrations 와

    python manage.py migrate --fake-initial 수행하고 진행하시기 바랍니다.

  - Database 테이블이 초기화 상태이어야 합니다. 데이터가 있으면 이 에러메시지가 발생합니다. 좀 이상한데,, 데이터가 존재하는 경우 연동을 어떻게 시키는지 아시는 분께서 답글좀 달아주시면 정말 감사하겠습니다.

 

오전 9시30분부터 시작해서 저녁 10:00 완료하기까지 알아낸 것 치고는 별거 없네요...

Python, Django, Oracle 버전 맞춘다고 지웠다 깔았다 계속하고, 특히 오라클 지우고 3번 다시 까느라 시간 다 보내고,

Django가 쉽다는데.....너무 어렵게 시작하는 것 같은...

 

 

 

그럼 이만~

 

(ps) 2020.06.03: 기존 오라클의 데이터가 있을 경우 makemigrations와 migrate는 없어야합니다. DB정보만 잘 입력해주면, 그리고 inspectdb만 잘 끌어오면 기존 데이터는 접근 가능합니다.

 

(ps) 2021.11.19: 특정 테이블만 가져오기

# python manage.py inspectdb --database=디비명 테이블명1 테이블명2 > 폴더이름/models.py 

python manage.py inspectdb --database=default TEMP TEMP2 > ./models/models.py

 

반응형
반응형

Django에 CSS를 적용하는 방법은 어렵지 않은데... 적용하려면 저같은 초보한테는 쉽지않네요. 

우선 기록으로 남겨놓습니다.

 

1. mysite/setting.py의 STATIC정보를 수정/확인합니다.

STATIC_URL = '/static/'
STATICFILES_DIRS = ( os.path.join('static'), )

2. CSS파일 생성(style.css)하여 아래 경로에 위치시킵니다.

mysite/static/css/style.css

li a {

    color: green;

}

3. HTML에 static file 로딩부분을 추가해줍니다.

{% load static %}   # 버전에 따라 staticfiles
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{% static 'css/style.css' %}" />
    <title>Title</title>
</head>

 

~~끝~~

 

(ps)

각 앱별로 static파일을 정의해놓았을 경우, STATICFILES_DIR 대신

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

를 설정하고,

python manage.py collectstatic 명령어를 주어 /static/폴더내로 복사해주면 적용됨.

 
 
반응형

+ Recent posts