해쟈스토리

클론코딩 - 에어비앤비 1주차 본문

파이썬

클론코딩 - 에어비앤비 1주차

해쟈 2024. 9. 30. 02:18
728x90
반응형

환경 설정

mkdir airbnb-clone-backend
cd  airbnb-clone-backend
git init.

 

Poetry

파이썬  패키지를 설치하고 관리할 수 있게 해준다.

https://python-poetry.org 로 접속하여 documentation을 클릭하고 introduction으로 간다

 

windows powershell install instructions 부분에서 명령어를 복사한 뒤 설치해준다.

(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

 

가상환경을 만들기 위해 아래 명령어를 입력한다.

poetry init

 enter를 입력해서 기본값으로 하고 다만, 라이센스는 MIT로 한다.

yes or no로 선택하는 부분은 no로 모두 선택한다.

이렇게 하면 pyproject.toml이라는 파일이 생긴다. 위에서 설정한 내용이 들어 있다. 

 

이제 django 패키지 설치를 해보자

poetry add django

 

가상환경으로 진입은 어떻게 할 수 있을까? 아래 명령어로 가능하다.

poetry shell

 

Django 시작하기

django-admin startproject config .

config라는 이름으로 프로젝트를 시작할건데 현재 디렉토리에 프로젝트 폴더를 생성한다.

manage.py 파일이 자동으로 생성된다. django를 실행하기 위해 아래 명령어를 입력한다. 

 

python manage.py runserver

 

그러면 db.sqlite3 파일이 생긴다. 

 

 

admin page를 위한 데이터베이스 테이블 생성(migration)

loacalhost:8000/admin 으로 이동하면 error 가 발생한다. (no such table : django_session)

위에서 생성된 db.sqlite2에 djange_session이라는 table이 없기 때문에 발생하는 에러이다. 

그래서 table을 만들어야하는데 이때 migration을 해야한다.

 

아래 명령어를 실행하자

python manage.py migrate

 

Super user 생성

django 서버를 실행한 상태에서 다른 터미널에 아래와 같이 입력한다.

python manage.py createsuperuser

 

 

/admin 페이지를 접속한 뒤 위에서 생성한 super user로 로그인을 한다.

 

admin 페이지를 통해 별도 설정 없이 로그인 기록, 가입시기, 사용자 인증정보를 관리할 수 있다.

 

서버환경 설정

config 디렉토리에 settings.py에서 각종 환경을 설정할 수 있다.

서버의 시간을 설정하기 위해 TIME_ZONE을 UTC에서 Asia/Seoul로 변경한다.

LANGUAGE_CODE를 ko-kr로 변경한다.

 

Apps

python manage.py startapp houses

 

houses 폴더가 생기고 migraions 폴더가 안에 있다.

 

houses/models.py가있는데 여기에 모델을 만들어준다,  sql 문법없이 이 파일에  데이터 형식을 정의하면 테이블을 자동으로 생성해준다.

 

class House(models.Model):
    """Model Definition for Houses"""
    name = models.CharField(max_length=140)
    price = models.PositiveIntegerField()
    description = models.TextField()
    address = models.CharField(max_length=140)
    pets_allowed = models.BooleanField(default=True)

 

 

파일을 수정하고나면 자동으로 서버가 재시작이 되지않는데,이는 서버에서 houses라는 app을 아직 모르기 때문. 

따라서 config/settings.py를변경해준다. 

INSTALLED_APPS=[
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
     "houses.apps.HousesConfig"
]

 

 

admin page에서 위에서 정의한 model를 control할 수 있도록 houses/admin.py를 수정한다.

 

from django.contrib import admin
from .models import House

@admin.register(House) # admin 패널에 House라는 model을 등록
class HouseAdmin(admin.ModelAdmin):
	pass

 

admin page에서 houses 모델 페이지로 이동하려고 하면 페이지 에러가 발생한다. session_table이 없다던 에러와 같은 것으로 얘 역시 migration을 해줘야한다. 

 

먼저 migration을 만들어 준다

python manage.py makemigrations

 

위 명령어를 진행하면 houses/migrations라는 폴더에 파일이 생성된다.

이제 이 migration을 적용시켜보자

 

python manage.py migrate

 

즉 모델(데이터 형식)이 변경되면 그때마다 migration 만들고 migrate를 실행해줘야한다.

 

아무튼 이제는 admin page에서 houses data를 추가 수정이 가능하다.

admin page에서 house data를 추가하면 House object이런식으로 보이는데, 이는 클래스 객체를 print한값이다. 이를 좀더 pretty하게 할 수 있게 __str__ 메소드를 변경해주자. 

 

def __str__(self):
	return self.name

 

 

admin 페이지에서 user 패널을 보면 컬럼도 있고 필터도 있는데 house는 없다. 이걸 바꿔보자

houses/admin.py에서 HouseAdmin class를 변경하자.

 

from .models import House

@admin.register(House) # admin 패널에 House라는 model을 등록
class HouseAdmin(admin.ModelAdmin):
	list_display = ( # 보여주고 싶은 column list를 적어준다. model의 property중에
        "name",
        "price_per_night",
        "address",
        "pets_allowed"
    )

 

이제 컬럼이 보이고, 컬럼별로 정렬도 된다. 필터링도 넣어보자

from .models import House

@asmin.register(House) # admin 패널에 House라는 model을 등록
class HouseAdmin(admin.ModelAdmin):
	list_display = ( # 보여주고 싶은 column list를 적어준다. model의 property중에
        "name",
        "price_per_night",
        "address",
        "pets_allowed"
    )
    
    list_filter = ( # 어떤 컬럼으로 필터링 할지 적어준다.
        "price_per_night",
        "pets_allowed"
    )

 

이러면 우측 사이드바쪽에 필터링 부분이 생긴다.

 

이제 검색창을 추가해보자.

from .models import House

@asmin.register(House) # admin 패널에 House라는 model을 등록
class HouseAdmin(admin.ModelAdmin):
	list_display = ( # 보여주고 싶은 column list를 적어준다. model의 property중에
        "name",
        "price_per_night",
        "address",
        "pets_allowed"
    )
    
    list_filter = ( # 어떤 컬럼으로 필터링 할지 적어준다.
        "price_per_night",
        "pets_allowed"
    )
    
    search_fields = ("address", ) # 검색하고자 하는 컬럼을 적어준다.

 

 

vscode에서 import django 시 노란 밑줄 생기는 이유

가상환경 안에서 django를 설치해서 vscode가 django가 설치되어 있는지 인식을 못함.(global에서 찾기 때문에)

하단에 python을 누르고 가상환경 python을 선택하자.

 

User App(여기부터)

django에서 기본적으로 제공하는 user 모델 외에 다른 모델이 필요하다면?

두가지 방법이 존재한다. 예를 들어 프로필이 있는 유저를 만들고 싶다면, user는 그대로 두고 사용자의 사진정보를 가지고 있는 profile 모델을 만든다. 그리고 그걸 user와 연결하는 방법이 있다.

두번째는 user 모델을 custom하게 만들어서 django에서 기본적으로 제공하는 user 모델과 맞바꾸는 것이다.

 

이 작업들은 프로젝트 초기에 하는 것이 좋다. 릴리즈 된 이후에도 변경이 가능하지만 자동으로 지원이 안되는 부분이 있어서 더 복잡해지기 때문이다. 그래서 프로젝트를 시작하면 custom user model로 바꾸는 것을 습관화 하자

 

python manage.py startapp users

 

users/models.py를 수정하자

 

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
	pass

 

그리고 나서 custom user model을 쓴다는 것을 django에게 알려주자

config/settings.py에 아래와 같이 추가한다.

# Auth
AUTH_USER_MODEL = "users.User"

 

그리고 같은 파일에 CUSTOM_APPS 부분을 추가한다.

CUSTOM_APPS = ["houses.apps.HousesConfig", "users.apps.UsersConfig",]

SYSTEM_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

# Application definition
INSTALLED_APPS = SYSTEM_APPS + CUSTOM_APPS

 

db.sqlite3 파일을 삭제하고 houses/migrations내에 0001~000X.py들을 삭제한다.

그리고나서 migration을 만들어 준다

python manage.py makemigrations

 

그리고 나서 migrate한다

python manage.py migrate

 

 

위에서 학습을 위해 했던 것들을 제거한다 houses/admin.py

from django.contrib import admin
from .models import House


@admin.register(House)
class HouseAdmin(admin.ModelAdmin):

    list_display = ("name", "price_per_night", "address", "pets_allowed")
    list_filter = ("price_per_night", "pets_allowed")

 

이제 user admin 페이지를 위해 users/admin.py 파일을 작성하자

 

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User


@admin.register(User)
class CustomUserAdmin(UserAdmin):
    pass

 

위에서 db를 지웠기 때문에 다시 super user를 생성해준다

python manage.py createsuperuser

 

이제 user Model을 진짜로 만들어보자

from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    first_name = models.CharField(max_length=150, editable=False)
    last_name = models.CharField(max_length=150, editable=False)
    name = models.CharField(max_length=150)
    is_host = models.BooleanField()

 

그러고 나서 miogration을 만들자.

 

그러면 위와 같이 에러 메세지가 나온다. 여기서 2를 선택하자

우리는 name과 is_host 라는  필드를 만들었는데 django에서 기본적으로 제공하는 user 모델(우리가 만든 customModel 말고) 에는 해당 필드가 없기 때문이다. 데이터 베이스 안에 name이랑 is_host가 있다고 인지했는데 정작 없는 상태.

 

bloean Field는 Null이 될수 없는 필드이다. 근데 현재 이미 user 데이터 베이스가 있는 상태인데, 여기서 is_host 필드를 추가하려고 하니까 이미 있는 데이터 row들은 null로 채우면서 필드가 생성된다. 근데 해당 필드 속성상 null이면 안된다.

그래서 추가할때 이미 있던 유저들의 데이터는 is_host부분을 뭘로 채울지 선택해 줘야한다.

 is_host = models.BooleanField(default=False)

 

 위와 같이 말이다.

최종적으로 아래와 같이 되야한다.

from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    first_name = models.CharField(max_length=150, editable=False)
    last_name = models.CharField(max_length=150, editable=False)
    name = models.CharField(max_length=150, default="")
    is_host = models.BooleanField(default = False)

 

 

admin 페이지에서 보면 아래와 같이 에러가 발생한다.

 

 

 

models.py에서 first_name, last_name에서 editable을 false로 설정했다. 그런데 이 모델을 관리자페이지에서 관리하다보니 자연히 edit하게 되서 문제가 되는 것.

fieldset을 통해 바꿔보자. 이거는 순서도 정할 수 있지만 섹션 안에 필드를 넣어서 그 섹션에 제목을 붙일 수도 있다.

 

fields에서는 튜플로 기입한 순서로 정할 수 있다.(admin.py에서)

 

 

 

그러나 fieldsets은 섹션으로 관리가 된다.

 

 

 

위와 같이 Profile이라는 섹션이 생긴다.

 

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User


@admin.register(User)
class CustomUserAdmin(UserAdmin):
    fieldsets = (
        (
            "Profile",
            {
                "fields": ("username", "password", "name", "email", "is_host"),
                "classes": ("wide",),
            },
        ),
        (
            "Permissions",
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                ),
                "classes": ("collapse",),
            },
        ),
        (
            "Important Dates",
            {
                "fields": ("last_login", "date_joined"),
                "classes": ("collapse",),
            },
        ),
    )

    list_display = ("username", "email", "name", "is_host")

 

 

house의 owner를 입력해보자. 근데 이 owner는 user 모델(테이블)에서 가져온거. house/models.py

owner = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )

 

이걸 외래키라고 하는데 어디서 가져올지을  user모델의 user클래스를 가져온다고 명시해주고 on_delete 옵션을 무조건 설정해줘야한다. 어떤식으로 삭제될때 규칙을 따를지

유저가 삭제될때 유저가 등록한 house도 삭제되도록 설정하려면 CASCADE 옵션을 사용한다.

만약 유저가 지워졌지만 결제내역은 남겨두고 싶을 수도 있어(세금이라던지 문제로) 그럴 경우에는 SET_NULL로 설정한다.

 

migrations(usee, house), db를 지우고 makemigration후 mograte한다. 그다음 super user 다시 만들고..

 


user model에 profile을 위한 imageField를 넣어준다. users/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    first_name = models.CharField(max_length=150, editable=False)
    last_name = models.CharField(max_length=150, editable=False)
    profile_photo = models.ImageField()
    name = models.CharField(max_length=150)
    is_host = models.BooleanField(default=False)

 

이렇게 저장을 하면 터미널에 아래와 같은 에러가 발생한다.(python manage.py runserver)

C:\STUDY\Programing\Python\에어비앤비\airbnb-clone-backend\users\models.py changed, reloading.
Watching for file changes with StatReloader
Performing system checks...

Exception in thread django-main-thread:
Traceback (most recent call last):
  File "C:\Users\sungj\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Users\sungj\AppData\Local\Programs\Python\Python310\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\sungj\AppData\Local\pypoetry\Cache\virtualenvs\airbnb-clone-backend-yX6RVG4g-py3.10\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "C:\Users\sungj\AppData\Local\pypoetry\Cache\virtualenvs\airbnb-clone-backend-yX6RVG4g-py3.10\lib\site-packages\django\core\management\commands\runserver.py", line 134, in inner_run
    self.check(display_num_errors=True)
  File "C:\Users\sungj\AppData\Local\pypoetry\Cache\virtualenvs\airbnb-clone-backend-yX6RVG4g-py3.10\lib\site-packages\django\core\management\base.py", line 563, in check
    raise SystemCheckError(msg)
django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS:
users.User.profile_photo: (fields.E210) Cannot use ImageField because Pillow is not installed.
        HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".

System check identified 1 issue (0 silenced).

 

아직 pillow를 설치하지 않아서 imageField를 사용할 수 없어서 그렇다.

 

poetry add Pillow

 

이제 user model에 gender를 넣어본다고 생각해보자. gender는 여성, 남성으로 두개 중 하나를 선택하는 옵션 선택이라서 TextChoices를 상속받는 클래스를 만들어보자. value, label로 적는다. 그리고 gender 부분에 choices에 넣어준다.

 

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    class GenderChoices(models.TextChoices):
        MALE = ("male", "Male")
        FEMALE = ("female", "Female")

    first_name = models.CharField(max_length=150, editable=False)
    last_name = models.CharField(max_length=150, editable=False)
    profile_photo = models.ImageField()
    name = models.CharField(max_length=150)
    is_host = models.BooleanField(default=False)
    gender = models.CharField(max_length=10, choices=GenderChoices.choices)

 

gender와 동일하게 language, currancy도 해보자.

 

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    class GenderChoices(models.TextChoices):
        MALE = ("male", "Male")
        FEMALE = ("female", "Female")

    class LanguageChoices(models.TextChoices):
        KR = ("kr", "Korean")
        EN = ("en", "English")

    class CurrencyChoices(models.TextChoices):
        WON = "won", "Korean Won"
        USD = "usd", "Dollar"

    first_name = models.CharField(max_length=150, editable=False)
    last_name = models.CharField(max_length=150, editable=False)
    avatar = models.ImageField()
    name = models.CharField(max_length=150)
    is_host = models.BooleanField(default=False)
    gender = models.CharField(max_length=10, choices=GenderChoices.choices,)
    language = models.CharField(max_length=2, choices=LanguageChoices.choices,)
    currency = models.CharField(max_length=5, choices=CurrencyChoices.choices,)

 

db를 다시 지우고 migration 파일 지우고 다시 migration을 만들고 migrate한다. 그리고 superuser를 다시 만든다.

그러나 admin page에서는 model설정한게 보이지 않는다. admin.py를 수정안했으니까!

 

그래서 admin 부분에 fieldsets의 field 부분에 추가해준다.

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User


@admin.register(User)
class CustomUserAdmin(UserAdmin):
    fieldsets = (
        (
            "Profile",
            {
                "fields": ("avatar", "username", "password", "name", "email", "is_host", "gender", "language", "currency", ),
                "classes": ("wide",),
            },
        ),
        (
            "Permissions",
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                ),
                "classes": ("collapse",),
            },
        ),
        (
            "Important Dates",
            {
                "fields": ("last_login", "date_joined"),
                "classes": ("collapse",),
            },
        ),
    )

    list_display = ("username", "email", "name", "is_host")

 

이제 admin에서는 우리가 설정한 model들이 보인다. 

그런데 avatar를 설정안하고 저장하면 저장이 안된다. required라서. 그래서 이걸 옵셔널하게 바꾸기 위해서는 model에서 blank=True를 해준다.

avatar = models.ImageField(blank=True)

 

Rooms model

python manage.py startapp rooms

 

settings.py에서 CUSTOM_APPS을 추가한다.

CUSTOM_APPS = [
    "houses.apps.HousesConfig", 
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig"
    ]

 

 

아래와 같이 model을 정의한다.

from django.db import models

class Room(models.Model):
    """Room Model Definition"""
    class RoomKindChoices(models.TextChoices):
        ENTIRE_PLACE = ("entire_place", "Entire place")
        PRIVATE_ROOM = ("private_room", "Private Room")
        SHARED_ROOM = "shared_room", "Shared Room"

    class Amenity(models.Model):
        """Amenity Definiton"""
        name = models.CharField(
            max_length=150,
        )
        description = models.CharField(
            max_length=150,
            null=True,
        )

    country = models.CharField(max_length=50, default="한국")
    city = models.CharField(max_length=80, default="서울")
    price = models.PositiveIntegerField()
    rooms = models.PositiveIntegerField()
    toilets = models.PositiveIntegerField()
    address = models.CharField(max_length=250)
    pet_friendly = models.BooleanField(default=True)
    kind = models.CharField(max_length=20, choices=RoomKindChoices,)
    owner = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )

 

 

n : n 모델은 어떻게? 

room과 owner은 n : 1 관계

amenity1~3가 있는데 이 모든 것을 room1, room2, room3 모두가 1~3의 amenity를 가지고 있을 수 있어.

 

이걸 위해 ManyToManyField가 존재한다.

from django.db import models


class Room(models.Model):
    """Room Model Definition"""

    class RoomKindChoices(models.TextChoices):
        ENTIRE_PLACE = ("entire_place", "Entire place")
        PRIVATE_ROOM = ("private_room", "Private Room")
        SHARED_ROOM = "shared_room", "Shared Room"

    country = models.CharField(max_length=50, default="한국")
    city = models.CharField(max_length=80, default="서울")
    price = models.PositiveIntegerField()
    rooms = models.PositiveIntegerField()
    toilets = models.PositiveIntegerField()
    address = models.CharField(max_length=250)
    pet_friendly = models.BooleanField(default=True)
    kind = models.CharField(
        max_length=20,
        choices=RoomKindChoices,
    )
    owner = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )
    amenities = models.ManyToManyField(
        "rooms.Amenity",
    )

class Amenity(models.Model):
    """Amenity Definiton"""

    name = models.CharField(
        max_length=150,
    )
    description = models.CharField(
        max_length=150,
        null=True,
    )

 

이제 room이 등록된 날짜와 마지막 업데이트 날짜를 가지고 싶음.

그래서 class Room에 아래를 추가한다.

created_at = models.DateTimeField(auto_now_add=True) # Object 생성시 시간을 넣는다 auto_now_add
updated_at = models.DateTimeField(auto_now=True) # auto_now object가 저장될 때마다 시간 넣기

 

그런데 이런 비슷한 코드를 amenity에도 넣고 싶다. 생성 날짜, 마지막 수정날짜. 모든 Model들이 이것을 가지고 있게 하고 싶음.

그래서 common app을 만들어 준다.

python manage.py startapp common

 

그리고 settings.py에서 CUSTOM_APPS를 추가한다.

CUSTOM_APPS = [
    "houses.apps.HousesConfig", 
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig"
    ]

 

그리고 나서 Room Model에 정의했던 date들을 잘라서 Common Model에 넣어준다.

from django.db import models

class CommonModel(models.Model):
    """ Common Model Definition """

    created_at = models.DateTimeField(auto_now_add=True) # Object 생성시 시간을 넣는다 auto_now_add
    updated_at = models.DateTimeField(auto_now=True) # auto_now object가 저장될 때마다 시간 넣기

 

근데 이 Common model로 table을 새로 생성하는 것은 원치 않는다. 이 모델은 다른 테이블 만들때 기본틀로 가져가는 거야. 그래서 Meta라는 클래스를 만들어 준다. django에서 model을 configure할때 사용하는 클래스이다.

abstract = True 해준다

from django.db import models

class CommonModel(models.Model):
    """ Common Model Definition """

    created_at = models.DateTimeField(auto_now_add=True) # Object 생성시 시간을 넣는다 auto_now_add
    updated_at = models.DateTimeField(auto_now=True) # auto_now object가 저장될 때마다 시간 넣기

    class Meta:
        abstract = True

 

그리고 나서 사용할 Model (room, houses 등등) 에서 import 를 해준다. 그리고 Room Class에 상속 받도록 해준다.

여기서는 room Model이다.

from django.db import models
from common.models import CommonModel


class Room(CommonModel):

 

이제 makemigration하고 migrate하자

그리고 나서 admin page에서 해당 모델들이 보일 수 있도록 room/admin.py를 수정하자

from django.contrib import admin
from .models import Room, Amenity


@admin.register(Room)
class RoomAdmin(admin.ModelAdmin):
    pass


@admin.register(Amenity)
class AmenityAdmin(admin.ModelAdmin):
    pass

 

admin 페이지에서 보면 amenity는 n:n으로 아래와 같이 +를 누르면 팝업창이 뜨고 저장할 수 있다.

 

Amenities를 추가하면 shampoo를 추가했는데 아래와 같이 나온다.

 

이걸 위해 __str__ 메소드를 수정한다.(room/models.py amenity class)

class Amenity(CommonModel):
    """Amenity Definiton"""

    name = models.CharField(
        max_length=150,
    )
    description = models.CharField(max_length=150, null=True, blank=True)

    def __str__(self):
        return self.name

 

 

room을 추가하고 나니 아래와 같이 room이름으로 보이지 않는다. 이것도 위와 같이 __str__메소드를 수정하자

 

 

 

<수정 후>

 

 

Model.py내에 클래스 명에 따라 왼쪽 사이드바에 s를 붙여서 나오게되는데 rooms는 맞지만 amenities는 복수형 s만 붙인다고 되는게 아니다.

그래서 이름을 바꾸고 싶다. 이럴때는 Amenity class내에 class Meta를 만들고 verbode_name_plural에 적어준다.

class Amenity(CommonModel):
    """Amenity Definiton"""

    name = models.CharField(
        max_length=150,
    )
    description = models.CharField(max_length=150, null=True, blank=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "Amenities"

 

 

필터나 필드(컬럼)이 보이지 않는데 이를 보이도록 설정하자.

from django.contrib import admin
from .models import Room, Amenity


@admin.register(Room)
class RoomAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "price",
        "kind",
        "owner",
        "created_at",
        "updated_at",
    )
    list_filter = (
        "country",
        "city",
        "pet_friendly",
        "kind",
        "amenities",
        "created_at",
        "updated_at",
    )


@admin.register(Amenity)
class AmenityAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "description",
        "created_at",
        "updated_at",
    )
    readonly_fields = (
        "created_at",
        "updated_at",
    )

 

 

experiences 모델

python manage.py startapp experiences

 

settings.py에서 CUSTOM_APPS에 추가

CUSTOM_APPS = [
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
]

 

experience model.py

from django.db import models
from common.models import CommonModel


class Experience(CommonModel):
    """Experience Model Definiiton"""

    country = models.CharField(
        max_length=50,
        default="한국",
    )
    city = models.CharField(
        max_length=80,
        default="서울",
    )
    name = models.CharField(
        max_length=250,
    )
    host = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )
    price = models.PositiveIntegerField()
    address = models.CharField(
        max_length=250,
    )
    start = models.TimeField()
    end = models.TimeField()
    description = models.TextField()
    perks = models.ManyToManyField(
        "experiences.Perk",
    )

    def __str__(self) -> str:
        return self.name


class Perk(CommonModel):
    """What is included on an Experience"""

    name = models.CharField(
        max_length=100,
    )
    details = models.CharField(
        max_length=250,
        blank=True,
        default="",
    )
    explanation = models.TextField(
        blank=True,
        default="",
    )

    def __str__(self) -> str:
        return self.name

 

 

experience admin.py

from django.contrib import admin
from .models import Experience, Perk
@admin.register(Experience)
class ExperienceAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "price",
        "start",
        "end",
        "created_at",
    )
@admin.register(Perk)
class PerkAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "details",
        "explanation",
    )

 

makemigrations하고 migrate하고

 

 

Category Model

python manage.py startapp categories

 

settings.py에 CUSTOM_APPS에 추가

CUSTOM_APPS = [
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
]

 

model 만들기

from django.db import models
from common.models import CommonModel

class Category(CommonModel):
    """Room or Experience Category"""
    class CategoryKindChoices(models.TextChoices):
        ROOMS = "rooms", "Rooms"
        EXPERIENCES = "experiences", "Experiences"
    
    name = models.CharField(max_length=50)
    kind = models.CharField(
        max_length=15,
        choices=CategoryKindChoices.choices,
    )
    def __str__(self) -> str:
        return f"{self.kind.title()}: {self.name}"
    
    class Meta:
        verbose_name_plural = "Categories"

 

 

근데 experience와 rooms model에 category가 없으니까 적용한다.

class Experience(CommonModel):
    """Experience Model Definiiton"""

    country = models.CharField(
        max_length=50,
        default="한국",
    )
    city = models.CharField(
        max_length=80,
        default="서울",
    )
    name = models.CharField(
        max_length=250,
    )
    host = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )
    price = models.PositiveIntegerField()
    address = models.CharField(
        max_length=250,
    )
    start = models.TimeField()
    end = models.TimeField()
    description = models.TextField()
    perks = models.ManyToManyField(
        "experiences.Perk",
    )
    category = models.ForeignKey(
        "categories.Category",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    def __str__(self) -> str:
        return self.name

 

room도 적용해준다.

그리고나서 makemigration, migrate해준다. 그리고나서 admin 파일 수정한다.

from django.db import models
from common.models import CommonModel

class Category(CommonModel):
    """Room or Experience Category"""
    class CategoryKindChoices(models.TextChoices):
        ROOMS = "rooms", "Rooms"
        EXPERIENCES = "experiences", "Experiences"
    name = models.CharField(max_length=50)
    kind = models.CharField(
        max_length=15,
        choices=CategoryKindChoices.choices,
    )
    def __str__(self) -> str:
        return f"{self.kind.title()}: {self.name}"
    class Meta:
        verbose_name_plural = "Categories"

 

 

Review Model 만들기

app 만들고 settings에 설정하기

python manage.py startapp reviews

 

CUSTOM_APPS = [
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
    "reviews.apps.ReviewsConfig",
]

 

model 수정

from django.db import models
from common.models import CommonModel
class Review(CommonModel):
    """Review from a User to a Room or Experience"""
    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )
    room = models.ForeignKey(
        "rooms.Room",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )
    experience = models.ForeignKey(
        "experiences.Experience",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )
    payload = models.TextField()
    rating = models.PositiveIntegerField()
    def __str__(self) -> str:
        return f"{self.user} / {self.rating}⭐️"

 

admin  수정

from django.contrib import admin
from .models import Review
@admin.register(Review)
class ReviewAdmin(admin.ModelAdmin):
    list_display = (
        "__str__",
        "payload",
    )
    list_filter = ("rating",)

 

makemigration, migrate

 

Wishlist Model 만들기

room, experiences를 가질 수 있다.

startapp wishlist , setting 설정하자.

python manage.py startapp wishlists
CUSTOM_APPS = [
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
    "reviews.apps.ReviewsConfig",
    "wishlists.apps.WishlistsConfig",
]

 

model 수정

from django.db import models
from common.models import CommonModel
class Wishlist(CommonModel):
    """Wishlist Model Definition"""
    name = models.CharField(
        max_length=150,
    )
    rooms = models.ManyToManyField(
        "rooms.Room",
    )
    experiences = models.ManyToManyField(
        "experiences.Experience",
    )
    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )
    def __str__(self) -> str:
        return self.name

admin 수정

from django.contrib import admin
from .models import Wishlist
@admin.register(Wishlist)
class WishlistAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "user",
        "created_at",
        "updated_at",
    )

 

makemigration, migrate하기

 

Bookings Model 만들기

startapp bookings 하고 settings 수정한다.

python manage.py startapp bookings
CUSTOM_APPS = [
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
    "reviews.apps.ReviewsConfig",
    "wishlists.apps.WishlistsConfig",
    "bookings.apps.BookingsConfig",
]

 

model 수정

from django.db import models
from common.models import CommonModel

class Booking(CommonModel):
    """Booking Model Definition"""
    class BookingKindChoices(models.TextChoices):
        ROOM = "room", "Room"
        EXPERIENCE = "experience", "Experience"
    kind = models.CharField(
        max_length=15,
        choices=BookingKindChoices.choices,
    )
    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
    )
    room = models.ForeignKey(
        "rooms.Room",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    experience = models.ForeignKey(
        "experiences.Experience",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    check_in = models.DateField(
        null=True,
        blank=True,
    )
    check_out = models.DateField(
        null=True,
        blank=True,
    )
    experience_time = models.DateTimeField(
        null=True,
        blank=True,
    )
    guests = models.PositiveIntegerField()
    def __str__(self):
        return f"{self.kind.title()} booking for: {self.user}"

 

admin  수정

from django.contrib import admin
from .models import Booking

@admin.register(Booking)
class Booking(admin.ModelAdmin):
    list_display = (
        "kind",
        "user",
        "room",
        "experience",
        "check_in",
        "check_out",
        "experience_time",
        "guests",
    )
    list_filter = ("kind",)

 

makemigrations, migrate 하자.

 

Medias Model 만들기

startapp medias 하고 setttings 설정하기

python manage.py startapp medias

 

CUSTOM_APPS = [
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
    "reviews.apps.ReviewsConfig",
    "wishlists.apps.WishlistsConfig",
    "bookings.apps.BookingsConfig",
    "medias.apps.MediasConfig",
]

 

model 작성

from django.db import models
from common.models import CommonModel


class Photo(CommonModel):
    file = models.ImageField()
    description = models.CharField(
        max_length=140,
    )
    room = models.ForeignKey(
        "rooms.Room",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    experience = models.ForeignKey(
        "experiences.Experience",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )

    def __str__(self):
        return "Photo File"


class Video(CommonModel):
    file = models.FileField()
    experience = models.OneToOneField(
        # foreignKey와 비슷한데, 이 동영상이 하나의 활동과 연결되면
        # 그 활동은 다른 동영상을 가질수 없게 된다.
        "experiences.Experience",
        on_delete=models.CASCADE,
    )

    def __str__(self):
        return "Video File"

 

admin 작성

from django.contrib import admin
from .models import Photo, Video

@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
    pass
@admin.register(Video)
class VideoAdmin(admin.ModelAdmin):
    pass

 

makemigration, migrate해준다

 

Direct_messages Model 만들기

startapp, settings 설정

python manage.py startapp direct_messages

 

CUSTOM_APPS = [
    "tweets.apps.TweetsConfig",
    "houses.apps.HousesConfig",
    "users.apps.UsersConfig",
    "rooms.apps.RoomsConfig",
    "common.apps.CommonConfig",
    "experiences.apps.ExperiencesConfig",
    "categories.apps.CategoriesConfig",
    "reviews.apps.ReviewsConfig",
    "wishlists.apps.WishlistsConfig",
    "bookings.apps.BookingsConfig",
    "medias.apps.MediasConfig",
    "direct_messages.apps.DirectMessagesConfig",
]

 

여기에 적는 이름은 apps 디렉토리내에 apps.py보면 된다. (여기서는 direct_messages/apps.py)

 

이제 model을 만들자

from django.db import models
from common.models import CommonModel


class ChattingRoom(CommonModel):
    """Room Model Definition"""

    users = models.ManyToManyField(
        "users.User",
    )

    def __str__(self):
        return "Chatting Room"


class Message(CommonModel):
    """Message Model Definition"""

    text = models.TextField()
    user = models.ForeignKey(
        "users.User",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    room = models.ForeignKey(
        "direct_messages.ChattingRoom",
        on_delete=models.CASCADE,
    )

    def __str__(self):
        return f"{self.user} says: {self.text}"

 

admin 만들자

from django.contrib import admin
from .models import ChattingRoom, Message

@admin.register(ChattingRoom)
class ChattingRoomAdmin(admin.ModelAdmin):
    list_display = (
        "__str__",
        "created_at",
        "updated_at",
    )
    list_filter = ("created_at",)
    
@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
    list_display = (
        "text",
        "user",
        "room",
        "created_at",
    )
    list_filter = ("created_at",)

 

chattingRoom이 아니라 Room으로 하게되면 이전에 만들었던 room 모델과 다이렉트 메세지를 위한 room 모델이 존재하고 이 두개 모두 user와 연결되서 문제가 발생한다.

 

그래서 채팅방 메세지 룸 모델의 이름을 바꿔줬다.

여기서 모델 이름이 중복이면 안된다고 착각할 수 있는데, 중복이어도 되는데 문제는 이 모델들이 같은 모델인 user와 연결되어 있다는게 문제인것이다

 

admin page에서 direct_messages 로 나오는게 싫다면, apps.py에서 변경 할 수 있다.

from django.apps import AppConfig


class DirectMessagesConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "direct_messages"
    verbose_name = "direct messages"
728x90
반응형

'파이썬' 카테고리의 다른 글

FastAPI로 정적파일 mount하기  (1) 2024.11.06
클론코딩 - 에어비앤비 2주차  (2) 2024.10.07