SQL의 꽃 JOIN이다.
4가지의 Join이 있으며, 이에대하여 정리해보자!

(INNER) JOIN

Returns records that have matching values in both table.
두개의 테이블에 모두 있는 값을 매칭한다!

SELECT Table1.Column1,Table1.Column2,Table2.Column1,....
FROM Table1
INNER JOIN Table2
ON Table1.MatchingColumnName = Table2.MatchingColumnName;

## 예제

위의 테이블에서 Employee Table의 EmpID와 Project를 연결해서 , 해당 인물의 프로젝트를 출력하자

SELECT Employee.EmpID, Employee.EmpFname, Employee.EmpLname, Projects.ProjectID, Projects.ProjectName
FROM Employee
INNER JOIN Projects ON Employee.EmpID=Projects.EmpID;

FULL (OUTER) JOIN

Returns all records when there is a match in either left or right table
양쪽을 서로 붙이는 것으로, 서로 없는것들에 대해 NULL로 채우면서 JOIN하는것.

SELECT Table1.Column1,Table1.Column2,Table2.Column1,....
FROM Table1
FULL JOIN Table2
ON Table1.MatchingColumnName = Table2.MatchingColumnName;

Employee 테이블과 Projects 테이블을 EmpID를 기준으로 FULL JOIN하였다.
그 결과 EMP에 없는 내용이지만, Project에 기록이 있을경우 EmpFname에 Null이 입력되고, 반대의 경우도 똑같이 다 연결되었다

LEFT JOIN

Left join returns all records from the left table (table1), and matched recored from the right table.
The recult is NULL from the right side, if there is no match.
왼쪽의 있는 테이블을 기준으로, 오른쪽 테이블을 붙이며, 오른쪽 테이블 중 매칭되지 않는 데이터는 NULL로 매핑된다.
인덱스 개수 : 왼쪽 테이블 행 수 만큼

SELECT Table1.Column1,Table1.Column2,Table2.Column1,....
FROM Table1
LEFT JOIN Table2
ON Table1.MatchingColumnName = Table2.MatchingColumnName;

왼쪽테이블 (Employee)을 기준으로 오른쪽 테이블 (Projects)를 붙였다. 그 결과, 왼쪽의 모든것은 다 따라왔으나, 그 데이터 중 오른쪽에 없을경우 Project테이블 내용이 NULL로 매핑되었다

RIGHT JOIN

Returns all records form the right table, and the matched records from the left table
오른쪽 테이블 (join하는 테이블)을 기준으로 붙임 -> 인덱스 개수 : 오른쪽 테이블 행 수 만큼

Select * from Orders o
RIGHT JOIN Customers c
ON o.CustomerID = c.CustomerID;

SELF JOIN

A Self join is a regular join, but the table is joined with itself.
스스로를 조인하는것으로, 활용하는 방법에 대해 조금 난이도있다고 생각된다.
가장 대표적인 케이스로, 어떠한 인적정보 테이블에서, 자신과 같은 도시에 사는 다른사람 쌍을 구해보는 쿼리를 짠다고 생각하면

SELECT e1.FirstName, e2.FirstName, e1.city
FROM employees e1, employees e2
where e1.EmployeeID <> e2.EmployeeID and e1.City = e2.City

where e1.EmployeeId <> e2.EmployeeId 의 부분은 자기 스스로와 매핑되지 않도록 하기위함이고,

and e1.city=e2.City 이 부분은 city가 같은 다른사람을 조인하기 위한 부분이다.

'SQL > SQL 공부' 카테고리의 다른 글

Programmers SQL 없어진 기록찾기  (0) 2021.06.05
기초 문법  (0) 2021.05.28

FROM

~~ FROM Table 어떤 테이블로부터 가져온다는 뜻

SELECT

말 그대로 선택 →

select * 하게되면 전부 가져오는것

SELECT column1, column2, ...
FROM table_name;

위와 같은 방식으로 작성하여 사용

DISTINCT

The SELECT DISTINCT statement is used to return only distinct (different) values.

유일한 값으로 가져온다는 뜻으로, 중복 없이 가져오게된다.

SELECT DISTINCT column1, column2, ...
FROM table_name;

Example

-- Write a statement that will select the City column from the Customers table.
SELECT City FROM Customers;

-- Select all the different values from the Country column in the Customers table.
SELECT DISTINCT Country FROM Customers;

WHERE

필터링 하는 것 . 원하는 조건을 걸 수 있다.

The WHERE clause is used to filter records.

The WHERE clause is used to extract only those records that fulfill a specified condition.

LIKE or NOT LIKE

특정 문자 또는 문자열을 포함하고 있는 값을 검색하고 싶을 때 사용.

% : 0개 이상의 문자열과 대치

_ : 임의의 한 개의 문자와 대치

%,_ 를 검색하고싶을땐? → @% , @_

--Genres에서 name이 B로 시작하는것 가져오기
select * from genres g
where g.Name like 'B%';

AND, OR and NOT Operators

생각대로 and , or , not 연산자를 사용하여 조건을 추가할 수 있다.

SELECT column1, column2, ...
FROM table_name
WHERE condition1 AND condition2 AND condition3 ...;

SELECT column1, column2, ...
FROM table_name
WHERE condition1 OR condition2 OR condition3 ...;

SELECT column1, column2, ...
FROM table_name
WHERE NOT condition;

-- Customers 테이블에서 Country는 Germany이고, City는 Berlin이나 Munchen인 records를 골라라
SELECT * FROM Customers
WHERE Country='Germany' AND (City='Berlin' OR City='München');
--Select all records where the City column has the value "Berlin".
SELECT * FROM Customers
WHERE City = Berlin;

--The following SQL statement selects all the customers from the country "Mexico", in the "Customers" table:
SELECT * FROM Customers
WHERE Country='Mexico';

--#Select all records where the City column has the value 'Berlin' and the PostalCode column has the value 12209.
Select * From Customers
Where City = 'Berlin' and PostalCode = 12209

SELECT * FROM Customers
Where 

IN, BETWEEN

The IN operator allows you to specify multiple values in a WHERE clause.

The IN operator is a shorthand for multiple OR conditions.

SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);

SELECT column_name(s)
FROM table_name
WHERE column_name IN (SELECT STATEMENT);

The BETWEEN operator selects values within a given range. The values can be numbers, text, or dates.

The BETWEEN operator is inclusive: begin and end values are included.

SELECT column_name(s)
FROM table_name
WHERE column_name BETWEEN value1 AND value2;

INSERT

The INSERT INTO statement is used to insert new records in a table.

새로운 레코드(행)을 추가할 때 사용한다.

INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);

INTO의 ( ... ) 와 VALUES의 ( ... )의 개수가 당연히 같아야한다.
해당 테이블의 column을 전부 채우지 않을 경우 null이 입력된다

NULL Values

A field with a NULL value is a field with no value.

SELECT column_names
FROM table_name
WHERE column_name IS NULL;

SELECT column_names
FROM table_name
WHERE column_name IS NOT NULL;

UPDATE

The UPDATE statement is used to modify the existing records in a table.

현재 있는 records를 업데이트한다

UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

Customers테이블에서, Country가 Norway인 레코드들의 City를 Oslo로 바꾼다

Set the value of the City columns to 'Oslo', but only the ones where the Country column has the value "Norway".

update Customers
set City = 'Oslo'
where Country = 'Norway';

DELETE

삭제하는것

말그대로 삭제이며, from으로 어디서 삭제할지 명시해줘야한다

DELETE FROM table_name WHERE condition;

MIN(), MAX()

최대 최소 뽑기

The MIN() function returns the smallest value of the selected column.

The MAX() function returns the largest value of the selected column.

SELECT MIN(column_name)
FROM table_name
WHERE condition;

SELECT MAX(column_name)
FROM table_name
WHERE condition;

COUNT(), AVG() and SUM()

개수새기, 평균, 합계 함수

SELECT COUNT(column_name)
FROM table_name
WHERE condition;

SELECT AVG(column_name)
FROM table_name
WHERE condition;

SELECT SUM(column_name)
FROM table_name
WHERE condition;

참조 : https://jhnyang.tistory.com/127

'SQL > SQL 공부' 카테고리의 다른 글

Programmers SQL 없어진 기록찾기  (0) 2021.06.05
SQL Join에 대하여  (0) 2021.05.28

여러 활성화 함수가 있지만, 가장 많이 쓰이는 몇개의 활성화 함수만 간단히 정리해보자

Sigmoid 시그모이드

$${\sigma(X)} = {1\over{1+e^{-x}}}$$

$$\sigma^\prime(X)= \sigma(x)(1-\sigma(x))$$

시그모이드 함수는 개별 뉴런이 내뱉어 주는 값을 S자 커브형태로 활성화시켜주며, 0~1 사이의 값을 뱉어준다.

다만, 입력값의 절대값이 5를 넘어갈 경우 그래디언트값이 상당히 작아짐 → 학습이 잘 안됨

자연상수 e 의 연산이 다소 무겁다는 단점이 있다.

하이퍼볼릭 탄젠트

시그모이드 함수의 크기와 위치를 조절한 함수이다.

$${tanh(x)} = {2\sigma(2x) - 1} = {{e^x-e^{-x}}\over{e^x + e^{-x}}}$$

$$tanh^\prime(x) = 1\ -\ tanh^2(x)$$

tanh(x) (왼쪽)    tanh(x) 의 미분(오른쪽)

하이퍼볼릭 탄젠트의 범위는 [-1,1]이다. 그리고 시그모이드와 달리 0을 기준으로 대칭이다.

위 두가지 특징이 시그모이드를 활성화함수로 쓸 때 보다 학습 수렴 속도가 빨라지게 한다.
tanh 또한, x가 5 이상 넘어가는 순간부터 기울기가 상당히 작아지는것을 볼 수 있다.

Rectified linear Unit (ReLU)

$f(x)\ = \ max(0,\ x)$

0보다 커지는 순간 값만큼 그대로 간다.

x가 양수이기만 하다면 기울기가 1로 일정하므로, 기울기 소실의 문제를 피할 수 있고, 미분이 편리해 계산이 매우 빠르다.

하지만 단점으로 x가 음수라면 기울기가 무조건 0이 된다는 단점이 있으며, 이를 보완하여 Leaky ReLU가 나왔다.

Leaky ReLU

$f(x) = max(0.01x, x)$

x가 음수일때 그래디언트가 0.01 이라는 점을 제외하고는 ReLU와 같은 특성을 지닌다.

 

 

참조

[Deep Learning-딥러닝]Activation Function-활성화 함수(Sigmoid, Hyperbolic Tangent, Softmax, ReLu)

https://ratsgo.github.io/deep learning/2017/04/22/NNtricks/

lambda

간편하게, 한줄로 함수를 만들어서 쉽게 쓸 수 있다.

아래는 일부러 2개의 인자를 받도록 lambda 함수를 만들고, 인자를 1개만 줘보았다.

positional argument 'y' 가 missing 되었다고 TypeError를 띄워준다

func = lambda x,y : x+y
func(10)

func = lambda x,y : x+y
list(map(func, [1,2],[2,3] ) )
# [3,5]
list(map(func, (1,2),(2,3)))
# [3,5]

2개의 인자를 받는 lambda 식에서 map으로 인자를 주려면 어찌줘야할까.. 해봤다

zip도 안먹히고, 하나로 묶으니 안먹힌다

아무래도 map함수자체가 iterable한 녀석한테 적용해주려다보니 이렇게 되는것같다.

a=[150,130, 80,60, 40,30, 20,15, 5,5]
list(map(lambda x: 
        'A' if x > 100
        else 'B' if x>50
        else 'C' if x>25
        else 'D' if x>10
        else 'E', a))

# ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'E', 'E']

lambda 안에서 굳이 이런식으로 if, else를 계속 쓸 수 있다.

filter

반복 가능한 객체에서 특정 조건에 맞는 요소만 가져온다.

filter에 지정한 함수의 반환값이 True일 때에만 해당 요소를 갖고오는것

a=[150,130, 80,60, 40,30, 20,15, 5,5]
list(filter(lambda x : x%30==0 , a))
# [150, 60, 30]

30으로 나누어 떨어지는수 (30의 배수)인 녀석들만 출력하도록 하였다

reduce

반복가능한 객체의 각 요소를 지정된 함수로 처리한 뒤 이전 결과와 누적하여 반환

from functools import reduce
a=[150,130, 80,60, 40,30, 20,15, 5,5]
reduce(lambda x,y : print(x,y) , a)
'''
150 130
None 80
None 60
None 40
None 30
None 20
None 15
None 5
None 5
'''

reduce(lambda x,y : x+y , [1,2,3,4,5,6,7,8,9])
# 45

맨 처음 람다의 인자로 index 0, 1이 주어진 후 그 뒤로는 y에만 전달된다.

그런데 굳이.. list의 컴프리헨션이 가능한 상황에서... 이걸 쓸 필요가 있을까 싶긴하다.

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
Python Generator 제너레이터, yield  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

참조 : https://wikidocs.net/16069

iterator를 생성해 주는 함수이며 함수 안에 yield 키워드를 사용

iterable한 순서가 지정됨

순서의 다음값이 필요에 따라 계산됨

함수 내부의 로컬 변수를 통해 내부 상태가 유지

무한한 순서의 객체를 모델링 할 수 있음

문득 든 생각이... 피보나치수열 만들때 이걸 쓰면 되는거 아닐까? 라는 생각이 들었고 ... 생각보다 잘 되는데 싶다

def fibo(a,b):
    fibolist=[a,b]
    while True:
        now = fibolist[-1] + fibolist[-2]
        fibolist.append(now)
        yield now

fibos = fibo(1,1)
fibolist=[1,1]
for i in range(10):
    fibolist.append(next(fibos))
fibolist
# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

이렇게 yield방식을 생각했을때 제일먼저 떠오른게 피보나치였고, 이게 과연 피보나치의 근본접근법이라고 여겨지는 Dynamic programming에서 Memoization과 얼마나 속도 차이가 날까? 테스트해보았다

# Yield를 사용했을 때
%%time
def fibo(a,b):
    fibolist=[a,b]
    while True:
        now = fibolist[-1] + fibolist[-2]
        fibolist.append(now)
        yield now

def getfibo(n):    
    fibos = fibo(1,1)
    fibolist=[1,1]
    for i in range(n-2):
        fibolist.append(next(fibos))
    return fibolist
tmp=getfibo(100*1000)
len(tmp)
# Memoization
%%time
def getfibo(n):
    fibos=[1,1]
    while len(fibos)<n :
        fibos.append(fibos[-1]+fibos[-2])
    return fibos
tmp = getfibo(100*1000)
len(tmp)

둘의 시간차이가 거의 없다...

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
lambda , filter, map  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

참조 : https://dojang.io/mod/page/view.php?id=2364

전역변수 지역변수

함수를 포함하여 스크립트 전체에서 접근할 수 있는 변수를 전역변수 (global variable)라고 부른다

g_val = 'g_val'
def v1():
    global gloval
    gloval = 'gloval'
    loval = 'local-val'
    print()
    print(g_val)
    print(gloval)
    print(loval)

v1()
# g_val
# gloval
# local-val

g_val은 전역변수로 선언되어있는 상태에서 v1이 실행, v1에서 gloval이라는 전역변수가 선언되고,

loval이라는 지역변수가 선언되었음

g_val = 'g_val'
def v1():
    global gloval
    gloval = 'gloval'
    loval = 'local-val'
    print()
    print(g_val)
    print(gloval)
    print(loval)

def v2(g_val, gloval):
    loval = 'v2 loval'
    print()
    print(g_val, gloval)
    print(loval)

v1()
v2(g_val, gloval)
'''
g_val
gloval
local-val

g_val gloval
'''

v1을 실행한 후, v2를 실행하였다.

g_val에 'g_val'이 담긴 상태에서, v1을 통해 gloval이 전역변수로 선언되었으며, 그렇기에 함수밖에서 v2로 gloval을 파라미터로 넘겨줄 수 있었다.

def argv2(**args):
    args['gloval'] = 'ddd'
    print()
    print(args)
    print(args['gloval'])
    print(gloval)
'''
{'gloval': 'ddd', 'dfdf': 'fdfd'}
ddd
gloval
'''

args로 받게되면, 딕셔너리로 담기는데, 이 때 여기서 gloval을 변경했을때는 어떨까? 고쳐보았다.

그 결과 args로 담겨진 gloval은 변경해도, 전역변수의 변경이 되지 않는것을 볼 수 있었다.

결국 마지막 print(gloval) 로, gloval을 확인해본 결과, 바뀌지않았다.

그냥 dictionary에서 Key = gloval 의 값이 바뀌어버린것으로만 보면 될것같다.

def v3(g_val, gloval):
    g_val = '333 changed'
    gloval = '33 changed groval'
    print()
    print(g_val, gloval)

v1()

v3(g_val, gloval)    ## 함수에서 전역변수를 받아서 변경해봄
print()
print(g_val, gloval)

'''
g_val
gloval
local-val

333 changed 33 changed groval

g_val, gloval
'''

v3에서 파라미터로 받은 변수를 변경한다면 어떻게될지 테스트해보았다.

g_val과, gloval을 둘 다 변경시켜서 v3내에서 출력한 결과 바뀌어서 출력된다.

그러나 외부에서 다시 g_val, gloval을 출력해본결과 바뀌지 않았다.

def v4():
    global g_val
    print(g_val)
    g_val = 'v4_gval'

v4()
print(g_val)
'''
g_val
v4_val
'''

이번엔 v4함수를 만들어서 , 그 안에서 global로 g_val을 불러오고나서 변경시켜보았다

잘 변경된다!

이로써 배우게 된 점 ....

전역변수를 설정하는것에 있어서 조심조심히 사용해야할것이며, 그것을 어떠한 함수 내에서 사용하면서 변경할 경우, 왠만하면 직접참조하지않는것이 좋을것같다. 정...변경이 필요하다면 return을 받아서 그걸 밖에서 저장하는 방식이 일관성있게 더 낫지않을까..싶다.
정말 부득이하게 어떠한 함수 내에서 전역변수를 변경하거나, 선언할 일이 생긴다면 global 키워드를 사용해야한다!

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
lambda , filter, map  (0) 2021.05.11
Python Generator 제너레이터, yield  (0) 2021.05.11
Object-Oriented Programming  (0) 2021.05.11

일단 OOP라는 개념에서 객체(Object)를 먼저 알아야한다.
객체란 어떤 속성(Attributes), 값(Value), 함수(Methods)를 갖는 데이터를 말한다.

캡슐화

내부 속성(변수), 함수를 하나로 묶어서 클래스로 선언하는 일반적인 개념
캡슐화 하지 않으면 특정 기능(함수, 변수)에 직접 접근하는 상황이 되고, 기능이 많아질수록 재사용성이 떨어진다.

이전에 JAVA를 배울때 접근제어에 대해 살짝 배운적이 있는데, Python 에서도 어느정도 접근제어가 가능하다!

name      # Public
_name     # Protected
__name    # Private
__asd__   # Public

어디서든 호출 가능한 Public과 동일 클래스나 인스턴스에서만 접근 가능한 Protected,
직접접근을 차단하는 Private를 선언할 수 있다.
그리고, 접미사(postfix)에 언더바가 2개 (__) 이상인 경우에 public으로 간주한다.!!
하지만... 이름만 복잡해질뿐, 기존의 객체지향언어에서 사용하던것만큼 엄격한 수준으로 접근관리가 이루어지지는 않는다..

상속과 포함

상속 Inheritance → 상위 클래스의 모든 기능(함수,변수)를 재사용 할 수 있음
포함 Composition → 다른 클래스의 일부 기능(함수)만을 재사용함

class Person:
    def __init__(self,age):
        self.age = age

    def printPerson(self):
        print(f'Person_printPerson , Age : {self.age}')
        return self.age

class Student:
    def __init__(self, age):
        self.age = age
        # Student가 Person에 포함되기 위해, Person 클래스에 대한 object 생성
        self.p = Person(self.age+100)       

    def personComposit(self,age):
        # 포함개념적용된 Student 클래스의 Person object의 함수
        return age, self.p.printPerson()  

# object 생성
s = Student(10) # 한 번 생성된 object는 파라미터가 변하지 않는 이상 출력값 또한 변하지 않는다.
p = Person(20)

s.p.printPerson()
s.personComposit(40)

위와같은 경우 Student의 p라는 속성으로 Person인스턴스를 하나 갖는다.

따라서 s.p.printPerson()s.personComposit(40) 의 결과가 같다

추상화

Object의 기능에 따라 추상클래스(상위)를 상송받아 개별적으로 하위클래스를 생성

실제 실행되는 기능은 선언된 추상 클래스를 상속받은 다른 클래스의 메소드에서 확인가능
→ @abstractmethod를 선언해서 사용해줘야함

프로그램이 복잡해 지는 경우 1차적인 설계를 위해 기능을 추상화 시켜놓고 활용여부는 차후 결정하기 위함

from abc import *
class People(metaclass=ABCMeta):
    @abstractmethod
    def character(self):
        pass
class Student(People):
    def character(self, pow, think):
        self.pow=pow
        self.think=think
        print(self.pow, self.think)

# p=People()
s=Student()
s.character('pow','think')

위의 People 클래스는 추상클래스로, 그 안에 def character 라는 추상메소드를 갖고있다.

그렇기에 Student에서 People을 상속받으며 character 의 상세 내용을 구체화해줘야 한다.

class Student_2(People):
    def __init__(self):
        super().__init__()

s2 = Student_2()
# TypeError: Can't instantiate abstract class Student_2 with abstract methods character

People 클래스를 상속받은 Student_2에서 character를 구체화하지않는다면 위와같은 타입에러를 볼 수 있다.

다형성

구션되는 하위클래스에 따라 클래스를 다르게 처리하는 기능 → 상위클래스의 기능을 변경하여 사용하느것 (그대로 사용하지 않음)

class A:
    def run(self):
        print('A run')

    def play(self):
        print("A play")

class B(A):
    def run(self):
        print("B run")
    def play(self):
        print("B play")

class C(A):
    def run(self):
        print("C run")

a=A()
b=B()
c=C()
c.play()
## A play

A ← B , C 의 형태로 상속한다

C에는 play() 메소드가 없는데, 이를 실행할경우 A.play() 를 실행한것과 같은 효과를 받는다

class C(B):
    def run(self):
        print("C run")
c=C()
c.play()
## B play

위의 상황에서 A ← B ← C로 상속관계가 이뤄진다.

이때 c.play()를 하게될 경우 B의 play() 가 실행된다.

참조 : https://velog.io/@seonj102/OOPS-파이썬

'Python' 카테고리의 다른 글

Python Decorator에 대하여 @ 데코레이터  (0) 2021.06.14
lambda , filter, map  (0) 2021.05.11
Python Generator 제너레이터, yield  (0) 2021.05.11
Global에 대하여  (0) 2021.05.11

'Saver' 카테고리의 다른 글

temporary img saver  (0) 2021.02.08

Database에 연결을 해서 메모리상에 존재하던 데이터를 저장해줘야 다시 켰을때 데이터를 보존할 수 있다.
Flask로 만든 웹서버에서 오고가는 데이터 또한 DB에 저장해주지 않으면, Flask를 다시 실행했을 때 데이터가 날아가게되어 처음부터 다시 입력하고 시작하는 등의 불편함이 동반된다.
이런것을 방지하기위해 DB에 연결하는것이고, DB에 연결하는것을 도와주는 도구를 이용해서 Flask에 연결해보자!

Application Factory를 지키면서 DB연결을 하자.

일단 DB에 만들 테이블을 먼저 생성해준다.

# models.usermodel.py
from kokoa import db

class User(db.Model):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key = True)
    username = db.Column(db.String(64), unique=True, nullable=False)
    password = db.Column(db.String(200), nullable=False)

    def __repr__(self):
        return f"User id : {self.id},  User name : {self.username}"
        # return f"User id : {self.id},  User name : {self.username}, E-mail : {self.email}"

(DB)모델은 kokoa에서 db를 import한다.
kokoa를 실행시키면 init.py가 실행되므로 init.py에 있는 db를 import한다고 생각하면 된다.

이렇게 테이블이 어떤모양을 갖고있을지 만들었으면 이제 처리해보자

일단 먼저 ORM을 적용시켜줘야한다. init.py 에 migrate부분을 추가해주고!

Table에 추가하기

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()

def create_app(config=None):
    app = Flask(__name__)


    db.init_app(app)
    migrate.init_app(app, db)
    from .models import user_model

위와같이 model을 추가해줬으면 이제 shell에서 명령어를 쳐줘야한다.

FLASK_APP=kokoa flask db init
FLASK_APP=kokoa flask db migrate
FLASK_APP=kokoa flask db upgrade

디비를 관리하는 초기파일을 migrations라는 디렉터리에 생성해주고, 이 파일을 알아서 관리해주므로 init을 해준 이후부터 모델에 테이블을 추가하거나 하는등 변화를 줬을땐, migrate와 upgrade만 해주면 된다!

relation table 관리하기!

만약 어떤 테이블이 1:N의 관계를 갖는다고 생각해보자.
지금 여기서는 User테이블이 있는 상태이고, 이 User가 어떤 (1:1)채팅방을 갖고있다고 생각했을때, 한명의 유저는 여러개의 채팅방을 가질 수 있다.
이러한 관계를 만든다고 하면,

class Room(db.Model):
    __tablename__ = 'room'
    id = db.Column(db.Integer, primary_key = True)
    user_id = db.Column(db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE'))
    company_id = db.Column(db.Integer(), db.ForeignKey('company.id', ondelete='CASCADE'))

    user = db.relationship('User', backref=db.backref('rooms', cascade='all, delete-orphan'))
    company = db.relationship('Company', backref=db.backref('rooms', cascade='all, delete-orphan'))
    def __repr__(self):
        return f"Room id : {self.id},  User - Company : {self.user_id} - {self.company_id}"

위를 보면 id는 기본이고, user_id 는 User테이블에 있는 id를 ForeignKey로 갖는다는것을 볼 수 있다. 여기서! ondelete='CASCADE'는 삭제연동을 의미한다. 이것을 설정해줌으로써 user를 삭제하게되면 자동으로 room까지 삭제되도록 하는것이다.
그리고 밑에 보면 user = db.relationship~부분의 backref를 볼 수 있다.
이는 역참조를 의미하며 user에서 user.rooms를 통해 채팅방을 참조할 수 있다.

일단 여기까지 DB모델을 설정해주는부분을 했으니, DB에 데이터를 추가하는 부분을 작업하자.
main_route.py라는 함수를 통해 url틍 통해 들어는것을 처리하며 이때 데이터를 같이 넘기면서 user를 추가하는 기능을 구현했다.

<!--index.html 부분 -->
<form name='form' id="login-form" method="post">
        <input name="username" type="text" required placeholder="Name"/>
        <input name = "password" type="text" required placeholder="Password"/>
        <input type='submit' value="Log In" onclick="javascript: form.action='/friends/';" />
        <input type='submit' value="Sign Up" onclick="javascript: form.action='/signup';"/>
        <a href="#">Find Account or Password</a>
    </form>

위와같이 Login과 signup을 할 수 있도록 form을 작성해주었다. method는 post로 설정하였다.
이후 받게되는 데이터는 아래와같이 처리되는데,

from flask import Blueprint, render_template, request, flash, session, g, url_for
from kokoa.models.user_model import User, Room 
from kokoa import db

bp = Blueprint('main', __name__)

@bp.route('/')
def index():
    return render_template('index.html')

@bp.route('/signup/', methods=['GET','POST'])
def signup():
    print('signup input : ',request.form)
    username=request.form['username']
    password=request.form['password']
    # form = UserCreateForm()
    if request.method=='POST': #form전송이 제대로 되었다면!
        #user가 이미 존재하는지 먼저 체크!
        user = User.query.filter_by(username=username ).first()

        if not user: #유저가 존재하지 않음 -> 생성
            user = User(username=username,\
                password=generate_password_hash(password))
            db.session.add(user)
            db.session.commit()
            return f'<script> alert("유저 생성 완료. ID : {username}, PASSWORD : {password}"); location.href="/" </script>'

        else: #유저가 이미 존재함 -> 뒤로가기
            return f'<script> alert("이미 존재하는 user입니다. 다른 이름을 입력하세요"); location.href="/" </script>'
            # flash('이미 존재하는 사용자입니다')
            # return '이미 존재하는 username입니다.'
    return f'<script> alert("오류. 다시 시작하세요"); location.href="/" </script>'

기본화면에서 form 제출을 POST로 받고 그렇게 받은 form에서 username과 password를 받았다.
db를 조회해서 user가 존재한다면 뒤로가기처리와 이미 존재한다는 알림을 띄우고, user가 db에 없다면 user생성을 하고 이를 알린다.
위와같이 user table에 데이터를 추가하는것을 만들었다!

+ Recent posts