본문 바로가기

BackEnd/Django

Django 외래키(Foreign Key)를 이용해 정보를 불러오는 방법

반응형

 


모델은 Owner, Brand, Car_Model, Car 총 네 가지 모델로 구성했습니다. 외래키 관계로 엮인 네 모델을 어떻게 template에서 참조하는지 알아보도록 하겠습니다.

# 전공자가 이해한 Django

기본적인 파일 구성은 아래와 같습니다. 불필요한 파일은 삭제하고 이번 실습에서 사용할 파일들만 남겨두었습니다.
앱 생성하는 과정은 생략하였습니다.

*저는 project를 생성할 때 setting으로 프로젝트를 우선 생성하고 프로젝트 폴더의 명을 변경하는 식으로 합니다. 따라서 settings.py 파일이 setting 폴더 아래에 가게 되고, 가장 상위 폴더는 project로 만들어 두었습니다.


1. settings.py

우선 settings.py 파일을 열어 생성된 앱을 등록하고, templates 폴더의 위치를 지정해주도록 하겠습니다.

# settings.py

# . . .

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'product',          # app 등록
    'django_extensions' 
    
    # django_extensions는 shell을 사용할 때 편하게 쓰기 위해 등록했습니다.
    
    # django_extensions를 추가하고 싶으신 분들은 
    # pip install django-extensions 로 설치하시기 바랍니다. 
]

# . . .

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        # . . .

 


2. models.py

모델은 Owner, Brand, Car_Model, Car 총 4가지 모델로 구성했습니다.

· Owner는 "차주"의 정보
· Brand는 자동차를 생산하는 "생산업체"의 정보
· Car_Model은 Brand들이 출시한 "차량"의 정보
· Car는 차주들이 소유하고 있는 "차량"의 정보

# models.py
from enum import unique
from django.db import models

# Create your models here.
class Owner(models.Model):
    
    name = models.CharField(max_length=128, unique=True)
    email = models.EmailField(unique=True)
    phone = models.CharField(max_length=128)
    
class Brand(models.Model):
    
    brand_name = models.CharField(max_length=128, unique=True)
    
class Car_Model(models.Model):
    
    brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
    model_name = models.CharField(max_length=128)

class Car(models.Model):
    
    car_number = models.CharField(max_length=128)
    owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
    car_model = models.ForeignKey(Car_Model, on_delete=models.CASCADE)

shell을 통해 객체를 생성하도록 하겠습니다. 우선, migration을 진행하도록 하겠습니다.

* python manage.py makemigrations

* python manage.py migrate 

만약 migrate를 진행했는데도 불구하고 테이블이 없다는 오류 메세지가 나온다면 --run-syncdb를 추가해서 migration을 진행해주시면 되겠습니다.

* python manage.py migrate --run-syncdb

VS code를 사용하시는 분들은 터미널에, 아닌 분들인 python shell을 따로 실행하시기 바랍니다.

앞서 django-extensions를 설치하고 진행했기 때문에 shell_plus로 실행시켰습니다.

shell_plus로 실행시키게 되면 아래와 같이 모델이 자동으로 import 되는 것을 확인하실 수 있습니다.

이제 본격적으로 객체를 생성해보도록 하겠습니다. 우리는 Owner, Brand, Car_Model, Car 객체를 각각 만들어야 합니다.

Car는 Owner, Car_Model과 Car_Model은 Brand와 각각의 ManyToOne(외래키) 관계를 갖고 있습니다. 따라서 Car 객체를 만들 때는 Owner의 객체와 Car_Model을, Car_Model을 만들 때는 Brand의 객체를 이용해야 합니다. 이해하시기 어려우신 분들은 우선 아래 과정을 따라가면서 이해해보도록 하시면 좋습니다.

첫번째로 Owner 객체를 생성하고, 각각 변수에 할당해보도록 하겠습니다.

객체 생성
변수 할당

두번째로 Brand 객체를 생성하고, 각각 변수에 할당하도록 하겠습니다.

객체 생성
변수 할당

세번째로 Car_Model 객체를 생성하고, 각각 변수에 할당하도록 하겠습니다.

Car_Model 객체 생성

Car_Model은 Brand 모델과 ManyToOne 관계를 갖고 있습니다. 즉, 브랜드는 많은 차량 모델을 갖고 있고, 각 차량 모델들은 하나의 Brand에 귀속되는 것이죠. "기아"의 차량은 "K3", "K5", "K7" 등과 같은 형태입니다.

각 객체의 ForeignKey 필드는 참조하고 있는 모델의 객체를 넣어주어야 합니다. 이전에 Brand 모델의 객체를 만들고 나서 변수를 할당했었습니다. 그 할당된 변수를 brand=변수로 넣어주어야 합니다. 만약 brand 부분에 그냥 "기아", "현대"와 같이 객체의 속성을 넣어주면 오류가 발생합니다.

안되는 예시
되는 예시

마지막으로 Car의 객체를 만들어보도록 하겠습니다. 여기서 고려해야 할 것이 Car는 Owner와 Car_Model을 각각 외래키로 갖고 있습니다. 쉽게 풀어보자면 "이재용"이라는 사람은 자동차를 여러 대 갖고 있을 수 있기 때문에 Car 와 ManyToOne 관계입니다. 또한 "소나타" 입장에서는 여러 사람이 소유해 자동차 번호가 여러 개 나올 수 있기 때문에 Car와 ManyToOne 관계입니다.

아래 객체 할당 부분을 보시겠습니다.

객체 생성

Car 객체를 생성해보았습니다. owner 속성에는 위에서 할당했던 변수인 "ljy"를 car_model 속성에는 할당된 변수가 아닌 직접 객체에 접근해서 내용을 전달했습니다.

이번에는 자동차의 모델을 바꿔서 만들어보도록 하겠습니다.

성공적으로 잘 만들어졌습니다. 이런 방식으로 여러 개의 객체를 만들어보시면 되겠습니다. #car_number 속성은 unique해야 합니다. 차량 번호가 여러 개일 수는 없기 때문입니다. 위에서 설정하는 걸 깜빡하고 넘어왔네요 ^^;; 이걸 보신 분들은 model의 car_number 속성에 unique=True로 설정하시기 바랍니다.

 


3. veiws.py

이제 veiws를 통해 get 요청을 받았을 때 객체 정보를 출력해보도록 하겠습니다.

# views.py
from django.views.generic import ListView
from .models import Car
from django.shortcuts import render

class Car_information(ListView):

    model = Car
    template_name = '../templates/template.html'
	
    # context 변수로 template에서 받을 이름을 설정합니다.
    context_object_name = 'car_info'
    
    # get 요청이 왔을 경우 실행될 queryset입니다.
    # 이 정보는 get 요청이 왔을 때 template에 전달됩니다.
    def get_queryset(self):
    	# 속성을 car_number="000허0000"로 하여 차량 번호가 "000허0000"와
        # 일치하는 객체를 출력합니다.
        # 추후에 이 부분을 수정해 다양한 값이 나타나도록 하겠습니다.
        return Car.objects.filter(car_number="000허0000")

 


4. urls.py

우선 product 앱 아래에 urls.py를 만들어 작성해주도록 하겠습니다.

# product/urls.py
from django.urls import URLPattern
from product import views
from django.urls import path

app_name = 'product'

urlpatterns = {
    path('list/', views.Car_information.as_view(), name='car_info')
}

이제 setting 폴더 아래에 있는 urls.py를 수정해주도록 하겠습니다. 

# setting/urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('product/', include('product.urls')),
]

 


5. template.html

{% for value in car_info %}
    <br>
    <br>
    
    <p>차량 번호 : {{ value.car_number }}</p>
    <p>소유주 : {{ value.owner.name }}</p>
    <p>차량 모델 : {{ value.car_model.model_name }}</p>

    <br>
    <br>
{% endfor %}

car_info를 불러와 value에 할당하고 해당 value 값에 car_number, owner의 name 등을 출력하도록 했습니다.

  • car_info : views.py 파일에서 context_object_name = 'car_info' 부분입니다.
  • value : views.py 파일에서 context로 넘겨준 것을 html에서 for문으로 풀어냅니다. 그때 할당되는 값입니다.
  • value.car_number : 현재 넘어온 값 중 for문으로 할당된 값의 car_number를 출력합니다.
  • value.owner.name : 현재 넘어온 값 중 for문으로 할당된 값의 Owner 객체의 name 값을 출력합니다.
  • value.car_model.model_name : 현재 넘어온 값 중 for문으로 할다된 값의 Car_Model 객체의 model_name 값을 출력합니다.

아래 두 값인 value.owner.name과 value.car_model.model_name을 이해하기 어려워하시는 분들을 위해 조금 더 자세하게 풀어보도록 하겠습니다.

value 값은 앞서 말했든 car_info로 넘어온 값 중 for문을 통해 풀어낸 값입니다. 우리는 views.py 파일에서 get_queryset으로 넘어올 데이터를 골라내었는데, 그 때 car_number가 "000허0000"인 값을 골라냈습니다. "000허0000"값을 가진 처음으로 설정했기 때문에 object 의 번호는 1번 입니다. 즉, 이걸 queryset 형식으로 풀어내자면

<Car : Car object (1)>

입니다. 만약 두번째로 입력한 값을 출력하면 ""와 같이 나타납니다. 이 객체의 속성으로 접근하려면 "객체.속성명" 으로 접근해야 합니다. 풀어보자면

<Car : Car object (1)>.car_number

와 같은 형태입니다. template 언어에서도 동일하게 속성으로 접근하려면 value.속성명으로 접근하면 됩니다. 따라서

value.car_number

가 됩니다. 이제 여기서 외래키로 묶여있는 속성들을 쿼리셋 형태로 풀어보면 다음과 같습니다.

<Car : Car object (1)><Owner : Owner object (1)>

우리가 Car 객체를 생성할 때 "000허0000" 차량을 소유하는 소유주를 "ljy"로 설정했었습니다. "ljy"는 우리가 Owner 객체를 생성할 때 제일 처음으로 생성했던 Owner 객체였기 때문에 객체 번호가 1번으로 주어졌습니다. 그렇기 때문에 위와 같은 형태의 모습이 나타난겁니다. 이제 Owner 객체의 name 속성을 출력해보겠습니다.

<Car : Car object (1)><Owner : Owner object (1)>.name

이렇게 보여드리면 이해가 좀 되실 거 같습니다. 이제 이걸 template 언어로 보면 아래와 같습니다.

value.owner.name

Car 객체의 속성인 owner에는 Owner 객체가 연결되어 있습니다. 따라서 위의 코드는 Car 객체의 owner 속성에 있는 Owner 객체를 찾아가서 name 값을 출력해줘 라고 이해할 수 있습니다.

이 정도면 이해하시지 않았을까 싶습니다. value.car_model.model_name도 마찬가지입니다. 그러면 차량 번호가 "000허0000"인 차량의 차량 brand를 보고 싶을 때는 어떻게 할까요?

value.car_model.brand.brand_name

<Car : Car object (1)><Car_Model : Car_Model object (1)><Brand : Brand object (n)>.brand_name

여기서 n값은 변수입니다. 어떤 차량을 할당했는지 독자분들마다 다를 것이기 때문에 n값으로 두었습니다.

 


6. 출력

이해가 되었다면 출력해보도록 하겠습니다.

*python manage.py runserver를 실행해서 설정한 url로 접근하겠습니다. 저와 동일하게 urls.py 파일을 작성하셨다면 url주소는 http://127.0.0.1:8000/product/list/ 입니다.

위와 같이 출력이 잘되네요 !

이제 하나의 값을 출력하는 형태는 되었습니다. 다음으로 owner가 소유한 차의 리스트를 받아와서 출력해보도록 하겠습니다.

다시 views.py 파일을 열어주세요. 거기서 get_queryset 의 filter 부분을 수정할겁니다. 지금은 car_number="000허0000" 으로 되어있을 텐데요. 이것을 owner__name="이재용" 으로 바꿔주도록 하겠습니다. 이것은 Car 객체의 owner 속성을 통해 연결되어 있는 Owner 객체의 name 속성에 접근해 "이재용"으로 되어있는 값들을 찾아 filter 해주는 코드입니다. "__" 언더바 두 개는 속성과 속성을 연결하는 역할을 합니다.

# views.py

# . . .

    def get_queryset(self):
        return Car.objects.filter(owner__name="이재용")

이제 다시 브라우저 창을 새로고침 해보도록 하겠습니다.

저는 "이재용"이라는 사람이 차량을 4개 갖도록 Car 객체를 생성해서 위와 같이 4개의 결과값이 나왔습니다. 아마 만드신 거에 따라 다 다를겁니다.

이번에는 brand가 "현대"인 차량을 필터링해서 찾아보도록 하겠습니다.

# views.py

# . . . 

    def get_queryset(self):
        return Car.objects.filter(car_model__brand__brand_name="현대")

원하는 결과값이 잘 나왔네요!

이제 외래키를 이용해서 자유롭게 정보를 불러오고 보낼 수 있게 되었습니다 !!
이것저것 추가해서 연습하시면 금방 익숙해지실 거 같습니다.

추가적으로 궁금하신 부분이 있으시면 댓글 남겨주세요. 빠르게 확인하고 답변드리도록 하겠습니다.

 


 

반응형