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>
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 =
회사에서 쓰는게 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은 나중에 알아보니 그냥 최신 깔아도 될 것 같습니다. 여기까진 그냥 고생한 내용이고..