본문 바로가기
개발/AWS

Django 앱 만들기 - Models method, upload image

by ny0011 2021. 1. 21.
반응형

* Models에 method를 만드는 기준

- FE, BE 모두에 사용되고 여기저기서 갖다 쓸 때

class Review(core_models.TimeStampedModel):

    """ Review Model Definition """

    review = models.TextField()
    accuracy = models.IntegerField()
    communication = models.IntegerField()
    cleanliness = models.IntegerField()
    location = models.IntegerField()
    check_in = models.IntegerField()
    value = models.IntegerField()
    user = models.ForeignKey(
        "users.User", related_name="reviews", on_delete=models.CASCADE)
    room = models.ForeignKey(
        "rooms.Room", related_name="reviews", on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.review} - {self.room}'

    def rating_average(self):
        avg = (self.accuracy +
               self.communication +
               self.cleanliness +
               self.location +
               self.check_in +
               self.value)/6
        return round(avg, 2)

이렇게 list_display에서 사용할 수 있음. __str__도 함수니까 이렇게 불러올 수 있다!

class ReviewAdmin(admin.ModelAdmin):

    """ ReviewAdmin Admin Definition """

    
    list_display = (
        "__str__",
        "rating_average",
    )

 

all()로 가져온 QuerySet도 list의 일종이라서 iterable임. for in 문을 사용해서 데이터 접근 가능.

    def total_rating(self):
        all_reviews = self.reviews.all()
        for review in all_reviews:
            print(review.rating_average())
        return 0
        
   3.17

 

django에도 timezone을 관리하는 패키지가 있당

docs.djangoproject.com/en/3.1/topics/i18n/timezones/

 

from django.utils import timezone

class Reservation(core_models.TimeStampedModel):
    def in_progress(self):
        now = timezone.now().date()
        return now > self.check_in and now < self.check_out
    in_progress.boolean = True
    in_progress.short_description = "Guest In Room"

    def is_finished(self):
        now = timezone.now().date()
        return now > self.check_out
    is_finished.boolean = True
    is_finished.short_description = "Checked Out"

 

함수를 list_display에 보여줄 수 있지만 list_filter에는 적용이 안되는 것 같다..ㅠ

class ReservationAdmin(admin.ModelAdmin):

    """ Reservation Admin Definition """

    list_display = (
        "room", "status", "check_in", "check_out", 'guest', "in_progress", "is_finished"
    )

    list_filter = ("status", )

 

- 이미지 업로드 관련해서 수정해보자

settings.py에서 이미지 관련 폴더의 root를 정해주고

docs.djangoproject.com/en/3.1/ref/settings/#media-root

# https://docs.djangoproject.com/en/3.1/ref/settings/#media-root
# https://docs.python.org/3/library/os.path.html#os.path.join
MEDIA_ROOT = os.path.join(BASE_DIR, "uploads")

models class에 정의된 ImageField에서 이미지를 upload 할 폴더 이름을 정해주면 된다.

-> uploads/avatars 안에 이미지가 저장됨

avatar = models.ImageField(upload_to="avatars", blank=True)

이미지의 URL을 지정해주면 URL로 이미지를 불러올 수 있다

docs.djangoproject.com/en/3.1/ref/settings/#media-url

MEDIA_URL이 non-empty면 반드시 "/"로 끝나야 함!

MEDIA_URL이 상대경로면 접두사로 서버에서 제공해주는 SCRIPT_NAME을 붙여야 함

(또는 / 를 붙여서 절대경로로 만든다)

MEDIA_URL = "/media/"

* config/settings.py 에서 개발 모드/릴리즈 모드를 변경할 수 있음

docs.djangoproject.com/en/3.1/topics/settings/#using-settings-in-python-code

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

개발 모드일 때 폴더 안의 파일들이 URL로 보이도록 할 것임

-> MEDIA_URL(media)와 MEDIA_ROOT(uploads)를 연결했다!

from django.conf import settings
from django.conf.urls.static import static

# 기존에 정의한 urlpatterns. 변수 이름 바꾸면 안됨!
urlpatterns = [
    path('admin/', admin.site.urls),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)

static 함수는 대강 이렇게 생겼다

URLPattern 으로 아래처럼 만든듯

def static(prefix, view=serve, kwargs):
	return [
		re_path(r'^%s(?P<path>.*)$' % re.escape(prefix.lstrip('/')), view, kwargs=kwargs),
    	]
   
def _path(route, view, kwargs=None, name=None, Pattern=None):
    if isinstance(view, (list, tuple)):
        # For include(...) processing.
        pattern = Pattern(route, is_endpoint=False)
        urlconf_module, app_name, namespace = view
        return URLResolver(
            pattern,
            urlconf_module,
            kwargs,
            app_name=app_name,
            namespace=namespace,
        )
    elif callable(view):
        pattern = Pattern(route, name=name, is_endpoint=True)
        return URLPattern(pattern, view, kwargs, name)
    else:
        raise TypeError('view must be a callable or a list/tuple in the case of include().')


>>> static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
[<URLPattern '^media/(?P<path>.*)$'>]

isinstance : docs.python.org/ko/3/library/functions.html#isinstance

view가 list나 tuple형이면 True, 아니면 False

callable : docs.python.org/ko/3/library/functions.html#callable

 

- image를 photo admin에서 보여주고 싶을 때

obj.file을 print해보면 string이 아니라 object임.

obj.file의 메소드 중 url을 사용해서 이미지의 url을 알 수 있다

<class 'django.db.models.fields.files.ImageFieldFile'>

img 태그를 리턴해서 웹에 보여질 수 있게 할 수 있다!

그치만 django가 string을 code로 인식하지 못하게 막고 있기 때문에 mark_safe 를 사용해줌

from django.utils.html import mark_safe

@admin.register(models.Photo)
class PhotoAdmin(admin.ModelAdmin):

    """ Photo Admin Definition """

    list_display = ('__str__', 'get_thumbnail',)

    def get_thumbnail(self, obj):
        # print(dir(obj.file))
        return mark_safe(f'<img src="{obj.file.url}" />')
    get_thumbnail.short_description = "Thumbnail"

 

- room admin에서 host를 검색할 수 있게 바꾸기

raw_id_fields : foreignkey나 manytomanyfield를 검색할 수 있게 Input 위젯으로 바꿔줌

docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields

-> 값이 너무 많아지면 찾기 어려울 때 사용

=> Host 이름 대신 id(primary key)로 나옴

class RoomAdmin(admin.ModelAdmin):

    raw_id_fields = ("host",)

 

- room admin에서 photo를 추가할 수 있게 photo admin 붙이기

InlineModelAdmin 을 사용하자

docs.djangoproject.com/en/3.1/ref/contrib/admin/#inlinemodeladmin-objects

TabularInline을 상속받은 class를 하나 만들어서

보고 싶은 admin class에 inlines 변수에 추가

class PhotoInline(admin.TabularInline):
    model = models.Photo

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

    inlines = [PhotoInline, ]

TabularInline 대신 StackedInline을 사용해도 됨. 기능은 똑같음

TabularInline : Model을 옆으로 길게 보여줌

StackedInline : Model을 밑으로 길게 보여줌

class PhotoInline(admin.StackedInline):
    model = models.Photo

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

    inlines = [PhotoInline, ]

 

- user admin에 room을 추가할 수 있게 room admin 붙이기

-> 위처럼 해주면 됨. 핵간단!

extra object이 기본값으로 3이길래 1로 줄여봄

from django.contrib import admin
from rooms import models as rooms_model

class RoomInline(admin.TabularInline):
    model = rooms_model.Room
	extra = 1

@admin.register(models.User)
class CustomUserAdmin(UserAdmin):
	inlines = [RoomInline, ]

 

요것도 참고해보자

realpython.com/customize-django-admin-python/

댓글