2021-03-23
최근 Django을 공부 하면서, 이것 저것 기록해 놓으려고 한다.
해당 글은 
Python 3.7.4, Django 3.1.7
을 기준으로 쓰여졌다
C:\django> python --version Python 3.7.4 C:\django> django-admin --version 3.1.7
Django 한글 메뉴얼: https://docs.djangoproject.com/ko/3.1/ 점프 투 쟝고: https://wikidocs.net/book/4223 시작하기 - Django 설치:
pip install django==3.1.7
- 프로젝트 생성:
django-admin startproject {명칭} .
config파일들이 들어 있기 때문에
django-admin startproject config .
식으로 해주면 좋다. startproject뒤에 오는 문구가 프로젝트의 이름 및 Django의 기본적인 셋팅 파일이 있는 디렉토리의 명칭이 된다. "."을 뒤에 붙일 경우 새로운 디렉토리를 생성하지 않고 해당 디렉토리에 만들어 진다. - 앱 생성:
django-admin startapp {명칭}
- 개발 서버 구동:
python manage.py runserver 8000
Django가 설치된 디렉토리에서 실행해야 한다. reunserver뒤에 붙은 숫자가 port번호가 된다. 0.0.0.0:8000 식으로 쓰면 외부에서도 접속 가능한 서버가 된다. config/settings.py - 언어와 시간을 한국으로 바꾸어 주면 한글로 바뀐다. - USE_TZ를 False로 설정해야 시간차이가 발생하지 않는다.
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ko-kr'

# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Seoul'

# USE_TZ = True
USE_TZ = False
- DATABASES 설정
#기본설정은 sqlite로 되어 있어 있다.
# python manage.py migrate 시에 sqlite 파일이 생성된다.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# mysql 설정
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_test',
        'USER': 'root',
        'PASSWORD': '1234',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

# mssql 설정
DATABASES = {
    'default': {
        'NAME' : 'name',
        'USER' : 'user',
        'PASSWORD' : 'password',
        'HOST' : 'host',
        'PORT' : '1433',
        'OPTIONS' : {
            'driver' : 'ODBC Driver 17 for SQL Server',
            'MARS_Connection' : True,
            'driver_supports_utf8' : True,
        },
    }
}
- static 폴더 설정
# 해당 설정후에 /static 폴더를 생성해야 한다.
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]
Auth 모델 커스텀 하기 - Django에서는 Auth_user 모델이 기본적으로 migration 되어 있다. - user의 기본적인 migration을 변경하려면, 최초에 migrate를 하기 전에 아래 작업을 진행해야 한다. - 한번이라도 migrate를 한 뒤에 아래를 진행하면, user_auth테이블이 이미 있어서, dependency(의존성) 오류가 떠버린다 - 한번이라도 migrate를 한 뒤에 Auth모델을 수정하려면, migration관련 파일을 수정하고, 삭제하고 초기화 시키는 방식이 있긴 하지만 추천하진 않는다. - 아래는 커스텀 Auth모델을 만드는 방법이다. 1. django-admin startapp 명령어로 새로운 앱을 만든다. - 앱 이름을 auth로 만들경우 Application labels aren't unique, duplicates: auth 에러가 뜨니 유의바람
C:\django> django-admin startapp authm
2. config\settings.py에 앱 등록 및 모델 정의 추가
AUTH_USER_MODEL = 'authm.User'

INSTALLED_APPS = [
	'authm.apps.AuthmConfig',
	.
	.
	.
]
3. authm\models.py에 새로 정의될 User모델 추가 - 아래와 같이 코딩할 경우, 이미 정의된 auth_user모델의 컬럼에 새로 정의한 컬럼이 추가되는 형태가 된다.
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
	level = models.CharField(null=True, max_length=2) # 권한
	department = models.CharField(null=True, max_length=100) # 부서
	position = models.CharField(null=True, max_length=100) # 직책
4. makemigrations후에 migrate 실행
C:\django> python manage.py makemigrations Migrations for 'authm': authm\migrations\0001_initial.py - Create model User C:\django> python manage.py migrate Operations to perform: Apply all migrations: admin, auth, authm, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK . . Applying auth.0012_alter_user_first_name_max_length... OK . . Applying sessions.0001_initial... OK
5. DB에 접속해서 확인해보면, 원래 생겨야 하는 auth_user테이블 대신, authm_user 테이블이 생긴 것을 확인 할 수 있다. - 차후 user모델에 새로운 컬럼을 추가 할 일이 생기면 위 모델을 통해 수정이 가능하다 config/urls.py - URL 맵핑 페이지 - path등록시 주소뒤에 "/"안붙이면 페이지 오류 뜸. - 일반적인 url 설정 방식 config\urls.py
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views

urlpatterns = [
	path('admin/', admin.site.urls),
	path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
	path('logout/', auth_views.LogoutView.as_view(), name='logout'),
	path('authm/', include('authm.urls')),
]
authm\urls.py(새로 만들어야함)
from django.urls import path
from . import views

app_name = 'authm'

urlpatterns = [
	path('test1', views.test1),
]
authm/views.py
from django.shortcuts import render
from django.http import HttpResponse

def test1(request):
	return HttpResponse('authm test1 page')
- 위와 같이 코딩시에 /authm/test1에 접속시 'authm test1 page'문구가 뜨게 된다 모델 -
python manage.py makemigrations
migration파일 생성 -
python manage.py migrate
migration파일을 바탕으로 실제 테이블 생성 - 모델 예제
from django.db import models
class test(models.Model):
	class Meta:
		db_table = 'test'
	idx = models.AutoField(primary_key=True)
	name = models.CharField(null=True, max_length=50)
	type = models.IntegerField(null=True)
	r_date = models.DateTimeField(null=True)
- 필드 타입 종류 링크1: https://docs.djangoproject.com/ko/3.2/ref/models/fields/ 링크2: https://brunch.co.kr/@ddangdol/4 - 쿼리 사용 예제
from django.http import HttpResponse
from django.utils import timezone
from authm.models import testbl_1

def orm_test(request):
	now = timezone.now()

	# delete - all()
	# 모든 행을 삭제
	q = testbl_1.objects.all()
	q.delete()

	# insert
	q = testbl_1(name='aaa', memo='memo1', r_date=now)
	q.save()

	# insert - used dictionary
	tmp1 = {
		'name': 'bbb',
		'memo': 'memo2',
		'r_date': now,
	}
	q = testbl_1(**tmp1)
	q.save()

	# insert - creat()
	tmp1 = {
		'name': 'ccc',
		'memo': 'memo3',
		'r_date': now,
	}
	testbl_1.objects.create(**tmp1)

	# select - all()
	# order_by에 -를 붙이면 해당 컬럼에 대해서 내림차순으로 가져옴
	q = testbl_1.objects.all().order_by('-idx')
	for v in q:
		# 기본적으로는 object형태로 되어 있음
		print(v.name, v.memo, v.r_date)

		# dictionary로 결과 출력
		v_d = v.__dict__
		print( v_d['name'], v_d['memo'], v_d['r_date'] )

		# print 결과
		# bbb memo2 2021-05-06 08:00:45.774422+00:00
		# aaa memo1 2021-05-06 08:00:45.774422+00:00

	# select - filter()
	q = testbl_1.objects.filter(name='aaa')
	for v in q:
		print(v.name, v.memo, v.r_date)
		# aaa memo1 2021-05-06 08:00:45.774422+00:00

	# select - filter() - used dictionary
	tmp1 = {
		'name': 'bbb',
		'memo': 'memo2',
	}
	q = testbl_1.objects.filter(**tmp1)
	for v in q:
		print(v.name, v.memo, v.r_date)
		# bbb memo2 2021-05-06 08:05:04.338555+00:00

	# select - latest()
	# filter는 검색결과가 없으면 []을 반환해서 상관 없지만,
	# latest(), get()은 반환결과가 없으면 DoesNotExist오류가 뜨기 때문에 try문 필수
	try:
		max = testbl_1.objects.latest('idx')
		max_idx = max.idx+1
	except testbl_1.DoesNotExist:
		max_idx = 1

	print(max_idx)

	# select - get()
	# get()은 단일 결과만 가져오기 때문에 결과가 2개 이상이면, MultipleObjectsReturned오류가 뜬다
	try:
		ddd = testbl_1.objects.get(name='ddd')
		# ddd = testbl_1.objects.get(memo__icontains='memo')
		c_name = ddd.name
	except testbl_1.DoesNotExist:
		c_name = 'DoesNotExist'
	except testbl_1.MultipleObjectsReturned:
		c_name = 'MultipleObjectsReturned'

	print(c_name) # Nothing

	# update - get()
	# 하나의 요소만 가져와서 update 하는 방식
	q = testbl_1.objects.get(name='aaa')
	q.name = 'abc'
	q.save()

	# update - filter()
	# 여러개 결과를 for문으로 update 하는 방식
	q = testbl_1.objects.filter(memo__icontains='memo') # memo like검색
	for v in q:
		v.name = 'ddd'
		v.save()

	# update - filter(), update()
	# 여러개 결과를 한번에 update 하는 방식
	update_arr ={
		'name': 'eee'
	}
	testbl_1.objects.filter(memo__icontains='memo').update(**update_arr)

	return HttpResponse('orm_test page')
- 조건 키워드 - 참고링크: https://brownbears.tistory.com/63
 키워드설 명사용예

 __lt / __gt

__lte / __gte

보 다 작다 / 보다 크다

같거나 보다 작다 / 같거나 보다 크다

id가 1보다 큰 자료 검색

>>> Department.objects.filter(id__gt=1)

[<Department: Computer Science>]

 __in주어진 리스트 안에 존재하는 자료 검색

id 가 2, 3, 5 인 자료 검색

 >>> Department.objects.filter(id__in=[2, 3, 5])
[<Department: Computer Science>]

 __year / __month / __day해당 년도, 월, 일 자료 검색>>>Entry.objects.filter(pub_date__year=2005)
__isnull해 당 열의 값이 null 인 자료  검색

>> Department.objects.filter(dName__isnull=True)

[]

 __contains / __icontains

해당 열의 값이 지정한 문자열을 포함하는 자료 검색

(__icontains 는 대소문자를 구별하지 않음)

>>> Department.objects.filter(dName__contains='puter')

[<Department: Computer Science>]

 __startswith / __istartswith

해당 열의 값이 지정한 문자열로 시작하는 자료 검색

(__istartswith 는 대소문자를 구별하지 않음)

>>> Department.objects.filter(dName__startswith='Com')
[<Department: Computer Science>]
 __endswith / __iendswith

해당 열의 값이 지정한 문자열로 끝나는 자료 검색

(__iendswith 는 대소문자를 구별하지 않음)

>>> Department.objects.filter(dName__contains='nce')
[<Department: Computer Science>]
 __range

문 자, 숫자, 날짜의 범위를 지정함

(SQL의 BETWEEN에

>>> Department.objects.filter(id__range=(2, 10))
- User 테이블 ORM 예제
from django.http import HttpResponse
# from django.contrib.auth.models import User # 기본으로 제공되는 User
from authm.models import User # 새롭게 정의한 User
from django.contrib.auth.hashers import make_password

def user_insert(request):
	insert_arr = {
		'password': make_password('1234'),
		'is_superuser': 0,
		'username': 'aaa',
		'first_name': 'f_name',
		'last_name': 'l_name',
		'email': 'aaa@blix.net',
		'is_staff': 1,
		'is_active': 1,
		'level': 1,
		'department': 'team1',
		'position': 'Senior',
	}

	User.objects.create(**insert_arr)
	return HttpResponse('user insert')

def user_update(request):
	update_arr = {
		'password': make_password(request.GET['pwd']),
		'position': request.GET['position'],
	}

	User.objects.update(**update_arr)
	return HttpResponse('user update')

def user_delete(request):
	User.objects.filter(id=1).delete()
	return HttpResponse('user delete')
- datatables의 serverSide옵션일때의 ORM
from django.db.models import Q
from datas.models import dt_tbl1

def all_list(request):
	tmp_data = {
		"data":[],
		"diagnosis":{},
	}

	p_start = int(request.POST['start'])
	p_end = p_start+int(request.POST['length'])
	order_col = request.POST['columns['+request.POST['order[0][column]']+'][name]']
	if request.POST['order[0][dir]'] == 'desc':
		order_col = '-'+order_col
	if request.POST['search[value]'] == '':
		tmp_data['recordsFiltered'] = dt_tbl1.objects.count()
		q = dt_tbl1.objects.all().order_by(order_col)[p_start:p_end]
	else:
		val1 = request.POST['search[value]']
		q_filter = Q()
		or_filters = {}
		for i in range(1,41):
			or_filters['a'+str(i)+'__icontains'] = val1

		for item in or_filters:
			q_filter |= Q(**{item:or_filters[item]})

		q = dt_tbl1.objects.filter(q_filter).order_by(order_col)[p_start:p_end]
		tmp_data['recordsFiltered'] = dt_tbl1.objects.filter(q_filter).count()

	for v in q:
		tmp_d = {
			"idx": v.idx,
			"r_date": str(v.r_date)[:19],
		}

		v_d = v.__dict__
		for i in range(1,41):
			j = 'a'+str(i)
			tmp_d[j] = v_d[j]

		tmp_data['data'].append(tmp_d)

	return tmp_data
shell -
python manage.py shell
해당 shell은 기본 shell과 다르게 해당 django디렉토리에 있는 포함된 것들을 사용 가능 admin -
python manage.py createsuperuser
/admin페이지에 접속 가능한 최고 관리자를 생성하는 명령어 로그인이 필요한 함수 -
@login_required(login_url='login')
view.py파일에서 적용
from django.contrib.auth.decorators import login_required

# @login_required 사용후 보낼 url을 설정해주면 된다.
@login_required(login_url='login')
def index(request):
	...
로그인시에 체크 하는 로직 - 아무 앱의 models.py에 만들어 놓으면 된다.
from django.contrib.auth.signals import user_logged_in
from authm.models import User

# 사용자의 ip를 가져오는 함수
def get_client_ip(request):
	x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
	if x_forwarded_for:
		ip = x_forwarded_for.split(',')[0]
	else:
		ip = request.META.get('REMOTE_ADDR')
		return ip

# 로그인시 실행될 함수
def login_chk(sender, user, request, **kwargs):
	update_arr ={
		'login_ip': get_client_ip(request)
	}
	User.objects.filter(username=user).update(**update_arr)

# 로그인시에 실행되도록 하는 부분
user_logged_in.connect(login_chk)